class Metasm::X86
fix autorequire warning
The ia32 aka x86 CPU currently limited to 16 and 32bit modes
Constants
- 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
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]
# File metasm/cpu/ia32/main.rb, line 207 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
wrapper to transparently forward ::new to Metasm::X86_64.new
# File metasm/cpu/ia32/main.rb, line 219 def self.new(*a) return X86_64.new(*a) if a.include? 64 and self == Ia32 super(*a) end
Public Instance Methods
returns a hash { :retval => r, :changed => [] }
# File metasm/cpu/ia32/decode.rb, line 1228 def abi_funcall { :retval => register_symbols[0], :changed => register_symbols[0, 3] } end
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_* 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
# 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
# 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
# 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
# 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
# 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
# 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
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
# 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
# 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
# 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
# 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
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
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
# File metasm/cpu/ia32/decode.rb, line 338 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
hash opcode_name => lambda { |dasm, di, *symbolic_args| instr_binding }
# File metasm/cpu/ia32/decode.rb, line 327 def backtrace_binding @backtrace_binding ||= init_backtrace_binding end
# File metasm/cpu/ia32/decode.rb, line 330 def backtrace_binding=(b) @backtrace_binding = b end
checks if expr is a valid return expression matching the :saveip instruction
# File metasm/cpu/ia32/decode.rb, line 904 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
returns true if the expression is an address on the stack
# File metasm/cpu/ia32/decode.rb, line 1006 def backtrace_is_stack_address(expr) Expression[expr].expr_externals.include? register_symbols[4] end
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 912 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
# File metasm/cpu/ia32/decode.rb, line 960 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
# 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
# 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
# File metasm/cpu/ia32/parse.rb, line 351 def check_reserved_name(name) Reg.s_to_i[name] end
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
# File metasm/cpu/ia32/decode.rb, line 1242 def code_binding(dasm, entry, finish=nil) entry = dasm.normalize(entry) finish = dasm.normalize(finish) if finish lastdi = nil binding = {} 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] binding[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] binding[:ip] = bt[lastdi.address, e.first, false] elsif not lastdi.opcode.props[:stopexec] binding[:ip] = lastdi.next_addr end end end binding.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 binding[reg] = Expression[val] } binding end
# File metasm/cpu/ia32/render.rb, line 22 def context ; {'set sz' => lambda { |s| @sz = s }} end
allocate a debug register for a hwbp by checking the list of hwbp existing in dbg
# File metasm/cpu/ia32/debug.rb, line 79 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
# File metasm/cpu/ia32/debug.rb, line 112 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
# 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
# File metasm/cpu/ia32/debug.rb, line 105 def dbg_disable_bphw(dbg, bp) nr = bp.internal[:dr] dr7 = dbg[:dr7] dr7 &= ~(3 << (2*nr)) dbg[:dr7] = dr7 end
# File metasm/cpu/ia32/debug.rb, line 74 def dbg_disable_bpx(dbg, bp) dbg.memory[bp.address, 1] = bp.internal[:previous] end
# 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
# 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
# File metasm/cpu/ia32/debug.rb, line 91 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
# File metasm/cpu/ia32/debug.rb, line 69 def dbg_enable_bpx(dbg, bp) bp.internal[:previous] ||= dbg.memory[bp.address, 1] dbg.memory[bp.address, 1] = "\xcc" end
# 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
# File metasm/cpu/ia32/debug.rb, line 142 def dbg_end_stepout(dbg, addr, di) di and di.opcode.name == 'ret' end
# File metasm/cpu/ia32/debug.rb, line 119 def dbg_evt_bpx(dbg, b) if b.address == dbg.pc-1 dbg.pc -= 1 end end
# File metasm/cpu/ia32/debug.rb, line 125 def dbg_find_bpx(dbg) return if dbg[:dr6] & 0x4000 != 0 pc = dbg.pc dbg.breakpoint[pc-1] || dbg.breakpoint[pc] end
# File metasm/cpu/ia32/debug.rb, line 131 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
# File metasm/cpu/ia32/debug.rb, line 29 def dbg_flag_list @dbg_flag_list ||= [:c, :p, :a, :z, :s, :i, :d, :o] end
retrieve the current function arguments only valid at function entry (eg right after the call)
# File metasm/cpu/ia32/debug.rb, line 186 def dbg_func_arg(dbg, argnr) dbg.memory_read_int(Expression[:esp, :+, 4*(argnr+1)]) end
# File metasm/cpu/ia32/debug.rb, line 189 def dbg_func_arg_set(dbg, argnr, arg) dbg.memory_write_int(Expression[:esp, :+, 4*(argnr+1)], arg) end
retrieve the current function return address to be called only on entry of the subfunction
# File metasm/cpu/ia32/debug.rb, line 177 def dbg_func_retaddr(dbg) dbg.memory_read_int(dbg_register_list[7]) end
# File metasm/cpu/ia32/debug.rb, line 180 def dbg_func_retaddr_set(dbg, ret) dbg.memory_write_int(dbg_register_list[7], ret) end
retrieve the current function return value only valid at function exit
# File metasm/cpu/ia32/debug.rb, line 168 def dbg_func_retval(dbg) dbg.get_reg_value(dbg_register_list[0]) end
# File metasm/cpu/ia32/debug.rb, line 171 def dbg_func_retval_set(dbg, val) dbg.set_reg_value(dbg_register_list[0], val) end
# 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
# File metasm/cpu/ia32/debug.rb, line 138 def dbg_need_stepover(dbg, addr, di) di and ((di.instruction.prefix and di.instruction.prefix[:rep]) or di.opcode.props[:saveip]) end
# File metasm/cpu/ia32/debug.rb, line 17 def dbg_register_flags @dbg_register_flags ||= :eflags end
# 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
# File metasm/cpu/ia32/debug.rb, line 11 def dbg_register_pc @dbg_register_pc ||= :eip end
# 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
# File metasm/cpu/ia32/debug.rb, line 14 def dbg_register_sp @dbg_register_sp ||= dbg_register_list[7] end
# 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
return (yield) a list of [addr, symbolic name]
# File metasm/cpu/ia32/debug.rb, line 147 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
# 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
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 1026 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
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
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
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
# 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
# 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
# File metasm/cpu/ia32/decompile.rb, line 196 def decompile_blocks(dcmp, myblocks, deps, func, nextaddr = nil) 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] until myblocks.empty? b, to = myblocks.shift if l = dcmp.dasm.get_label_at(b) stmts << C::Label.new(l) 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| dcmp.decompile_cexpr(Expression[Expression[*e].reduce], scope) } # 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 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| 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)) 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) 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), t) 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) 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] e = C::CExpression[ce[:eax], :'=', e, f.type.type] 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) 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)] args = [] end ret = C::Return.new(C::CExpression[fptr, :funcall, args]) class << ret ; attr_accessor :from_instr end ret.from_instr = di 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)) 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)) 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)) 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)) 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) 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]]] blk.statements << C::CExpression[ceb[:edi], :'=', [ceb[:edi], :+, [sz]]] blk.statements << C::CExpression[ceb[:esi], :'=', [ceb[:esi], :+, [sz]]] when 'sto' blk.statements << C::CExpression[[:*, [[ceb[:edi]], pt]], :'=', ceb[:eax]] blk.statements << C::CExpression[ceb[:edi], :'=', [ceb[:edi], :+, [sz]]] when 'lod' blk.statements << C::CExpression[ceb[:eax], :'=', [:*, [[ceb[:esi]], pt]]] blk.statements << C::CExpression[ceb[:esi], :'=', [ceb[:esi], :+, [sz]]] #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]]] stmts << C::While.new(C::CExpression[ceb[:ecx]], blk) #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) 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 } 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
# File metasm/cpu/ia32/decompile.rb, line 532 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
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] |= [:eax, :ecx, :edx] # 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 == :eax 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
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 << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void) # standard ABI end
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
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 1091 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
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 1205 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
returns a DecodedFunction suitable for :default uses disassembler_default_bt{for/bind}_callback
# File metasm/cpu/ia32/decode.rb, line 1216 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
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
patch a forward binding from the backtrace binding fixes fwdemu for push/pop/call/ret
# File metasm/cpu/ia32/decode.rb, line 794 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' fbd = fbd.dup sz = opsz(di)/8 esp = register_symbols[4] if i = fbd.delete(Indirection[esp, sz]) fbd[Indirection[[esp, :-, sz], sz]] = i 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
# File metasm/cpu/ia32/decode.rb, line 744 def get_backtrace_binding(di) a = di.instruction.args.map { |arg| case arg when ModRM, Reg, SimdReg; arg.symbolic(di) else arg end } 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
returns the condition (bool Expression) under which a conditionnal jump is taken returns nil if not a conditionnal jump backtrace for the condition must include the jump itself (eg loop -> ecx–)
# File metasm/cpu/ia32/decode.rb, line 732 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
# File metasm/cpu/ia32/decode.rb, line 816 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' a = di.instruction.args.first if dasm and a.kind_of?(ModRM) and a.imm and (a.s == sz/8 or a.s == 4) and not a.b and dasm.get_section_at(a.imm) return get_xrefs_x_jmptable(dasm, di, a, a.s*8) 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 #{di.address} #{di.instruction}" if $DEBUG [] end end
we detected a jmp table (jmp [base+4*idx]) try to return an accurate dest list
# File metasm/cpu/ia32/decode.rb, line 844 def get_xrefs_x_jmptable(dasm, di, mrm, sz) # include the symbolic dest for backtrack stuff ret = [Expression[mrm.symbolic(di)]] i = mrm.i if di.block.list.length == 2 and di.block.list[0].opcode.name =~ /^mov/ and a0 = di.block.list[0].instruction.args[0] and a0.respond_to? :symbolic and a0.symbolic == i.symbolic i = di.block.list[0].instruction.args[1] end pb = di.block.from_normal.to_a if pb.length == 1 and pdi = dasm.decoded[pb[0]] and pdi.opcode.name =~ /^jn?be?/ and ppdi = pdi.block.list[-2] and ppdi.opcode.name == 'cmp' and ppdi.instruction.args[0].symbolic == i.symbolic and lim = Expression[ppdi.instruction.args[1]].reduce and lim.kind_of? Integer # cmp eax, 42 ; jbe switch ; switch: jmp [base+4*eax] s = dasm.get_section_at(mrm.imm) lim += 1 if pdi.opcode.name[-1] == ?e lim.times { |v| dasm.add_xref(s[1]+s[0].ptr, Xref.new(:r, di.address, sz/8)) ret << Indirection[[mrm.imm, :+, v*sz/8], sz/8, di.address] s[0].read(sz/8) } l = dasm.auto_label_at(mrm.imm, 'jmp_table', 'xref') replace_instr_arg_immediate(di.instruction, mrm.imm, Expression[l]) # add 'case 1' comments cases = {} ret.each_with_index { |ind, idx| idx -= 1 # ret[0] = symbolic next if idx < 0 a = dasm.backtrace(ind, di.address) if a.length == 1 and a[0].kind_of?(Expression) and addr = a[0].reduce and addr.kind_of?(::Integer) (cases[addr] ||= []) << idx end } cases.each { |addr, list| dasm.add_comment(addr, "case #{list.join(', ')}:") } return ret end puts "unrecognized jmp table pattern, using wild guess for #{di}" if $VERBOSE di.add_comment 'wildguess' if s = dasm.get_section_at(mrm.imm - 3*sz/8) v = -3 else s = dasm.get_section_at(mrm.imm) v = 0 end while s[0].ptr < s[0].length ptr = dasm.normalize s[0].decode_imm("u#{sz}".to_sym, @endianness) diff = Expression[ptr, :-, di.address].reduce if (diff.kind_of? ::Integer and diff.abs < 4096) or (di.opcode.basename == 'call' and ptr != 0 and dasm.get_section_at(ptr)) dasm.add_xref(s[1]+s[0].ptr-sz/8, Xref.new(:r, di.address, sz/8)) ret << Indirection[[mrm.imm, :+, v*sz/8], sz/8, di.address] elsif v > 0 break end v += 1 end ret end
# 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
# 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
# File metasm/cpu/ia32/opcodes.rb, line 1056 def init_386 init_386_common init_386_only end
CPU family dependencies
# File metasm/cpu/ia32/opcodes.rb, line 1052 def init_386_common init_386_common_only end
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
# 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
# File metasm/cpu/ia32/opcodes.rb, line 1061 def init_387 init_387_only end
# 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
# File metasm/cpu/ia32/opcodes.rb, line 1076 def init_3dnow init_pentium init_3dnow_only end
# 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
# File metasm/cpu/ia32/opcodes.rb, line 1065 def init_486 init_386 init_387 init_486_only end
# File metasm/cpu/ia32/opcodes.rb, line 354 def init_486_only init_cpu_constants end
# 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
# File metasm/cpu/ia32/opcodes.rb, line 1127 def init_all init_avx2 init_3dnow_only init_vmx_only init_aesni_only end
# File metasm/cpu/ia32/opcodes.rb, line 1116 def init_avx init_sse42 init_avx_only end
# File metasm/cpu/ia32/opcodes.rb, line 1121 def init_avx2 init_avx init_fma_only init_avx2_only end
# 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
# 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
populate the @backtrace_binding hash with default values
# File metasm/cpu/ia32/decode.rb, line 345 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, :>>, 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] { eax => 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 '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', '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 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
# 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
# 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
initializes the @opcode_list according to @family
# File metasm/cpu/ia32/main.rb, line 225 def init_opcode_list send("init_#@family") @opcode_list end
# File metasm/cpu/ia32/opcodes.rb, line 1081 def init_p6 init_pentium init_p6_only end
# 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
# File metasm/cpu/ia32/opcodes.rb, line 1071 def init_pentium init_486 init_pentium_only end
# 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
# File metasm/cpu/ia32/opcodes.rb, line 1086 def init_sse init_p6 init_sse_only end
# File metasm/cpu/ia32/opcodes.rb, line 1091 def init_sse2 init_sse init_sse2_only end
# 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
# File metasm/cpu/ia32/opcodes.rb, line 1096 def init_sse3 init_sse2 init_sse3_only end
# 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
# File metasm/cpu/ia32/opcodes.rb, line 1106 def init_sse41 init_ssse3 init_sse41_only end
# 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
# File metasm/cpu/ia32/opcodes.rb, line 1111 def init_sse42 init_sse41 init_sse42_only end
# 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
# 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
# File metasm/cpu/ia32/opcodes.rb, line 1101 def init_ssse3 init_sse3 init_ssse3_only end
# 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
# 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
returns the list of ModRMs in the instruction arguments may be converted into Indirection through Metasm::Ia32::ModRM#symbolic
# File metasm/cpu/ia32/main.rb, line 256 def instr_args_memoryptr(i) i = i.instruction if i.kind_of?(DecodedInstruction) i.args.grep(ModRM) end
return the 'base' of the ModRM (Reg/nil)
# File metasm/cpu/ia32/main.rb, line 262 def instr_args_memoryptr_getbase(mrm) mrm.b || (mrm.i if mrm.s == 1) end
return the offset of the ModRM (Expression/nil)
# File metasm/cpu/ia32/main.rb, line 267 def instr_args_memoryptr_getoffset(mrm) mrm.imm end
define ModRM offset (eg to changing imm into an ExpressionString)
# File metasm/cpu/ia32/main.rb, line 272 def instr_args_memoryptr_setoffset(mrm, imm) mrm.imm = (imm ? Expression[imm] : imm) end
returns the list of Regs in the instruction arguments may be converted into symbols through Metasm::Ia32::Reg#symbolic
# File metasm/cpu/ia32/main.rb, line 249 def instr_args_regs(i) i = i.instruction if i.kind_of?(DecodedInstruction) i.args.grep(Reg) end
# File metasm/cpu/ia32/parse.rb, line 355 def instr_uncond_jump_to(target) parse_instruction("jmp #{target}") end
# 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
trace the stack pointer register across a function, rename occurences of esp+XX to esp+var_XX
# File metasm/cpu/ia32/decode.rb, line 1320 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
# 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
# File metasm/cpu/ia32/decode.rb, line 332 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
check if the argument matches the opcode's argument spec
# File metasm/cpu/ia32/parse.rb, line 239 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
# File metasm/cpu/ia32/parse.rb, line 177 def parse_argregclasslist [Reg, SimdReg, SegReg, DbgReg, TstReg, CtrlReg, FpReg] end
parses an arbitrary ia32 instruction argument
# File metasm/cpu/ia32/parse.rb, line 185 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
# File metasm/cpu/ia32/parse.rb, line 315 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
fixup the sz of a modrm argument, defaults to other argument size or current cpu mode
# File metasm/cpu/ia32/parse.rb, line 326 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
# File metasm/cpu/ia32/parse.rb, line 180 def parse_modrm(lex, tok, cpu) ModRM.parse(lex, tok, cpu) end
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…
# File metasm/cpu/ia32/parse.rb, line 146 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
# File metasm/cpu/ia32/parse.rb, line 161 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
# File metasm/cpu/ia32/decode.rb, line 296 def register_symbols REG_SYMS end
# File metasm/cpu/ia32/render.rb, line 18 def render ; [self.class.i_to_s[@val]] end
# 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
updates an instruction's argument replacing an expression with another (eg label renamed)
# File metasm/cpu/ia32/decode.rb, line 1011 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
# File metasm/cpu/ia32/main.rb, line 276 def shortname "ia32#{'_16' if @size == 16}#{'_be' if @endianness == :big}" end
returns a Reg/SimdReg object if the arg is a valid register (eg 'ax' => Metasm::Ia32::Argument.new(0, 16)) returns nil if str is invalid
# File metasm/cpu/ia32/main.rb, line 243 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
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
# File metasm/cpu/ia32/main.rb, line 233 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