class P2Align(Line): __slots__ = ('section', 'offset', 'bytelen', 'binary', 'symbols', 'line', 'pic', 'stack', 'dead') def __init__(self, symbols, line, **kargs): Line.__init__(self, symbols) self.line = line if 'section' in kargs: self.offset = kargs['section'] if 'offset' in kargs: self.offset = kargs['offset'] if 'binary' in kargs: self.binary = kargs['binary'] if 'binary' in kargs: self.bytelen = len(self.binary) def txt(self, asm_format=None): if asm_format is None: asm_format = self.symbols.arch.asm_format if asm_format == 'raw' and hasattr(self, 'binary'): from plasmasm.python.compatibility import hexbytes return "[.]nop %s[%s]" % ("".join(hexbytes(self.binary)), self.line) return "%s [%s]" % (self.line, "".join(hexbytes(self.binary))) return self.line def pack(self): if hasattr(self, 'binary'): return self.binary opname = 'nop' flow = None CPU = None rw = (set(), set()) def reg_name(self, r): return self.symbols.arch.reg_name(r) def reg_from_name(self, r): return self.symbols.arch.reg_from_name(r) def copy(self): new = self.__class__(self.symbols, self.line) if hasattr(self,'offset'): new.offset = self.offset if hasattr(self,'binary'): new.binary = self.binary if hasattr(self,'dead'): new.dead = self.dead.copy() return new
class Instruction(Line): __slots__ = ('line', 'opname', 'operands') CPU = None def __init__(self, symbols): Line.__init__(self, symbols) def from_txt(self, line): if line.startswith('\t'): line = line[1:] self.line = line r = re.match(r'\s*(\S+)\s*(.*)', line) if r: self.opname = r.groups()[0] self.operands = [s.strip(' ') for s in r.groups()[1].split(',')] return self def txt(self, asm_format=None): return self.line def set_asm_format(self, asm_format): pass set_asm_format = classmethod(set_asm_format) asm_format = None # Stubs... TODO rw = (set(), set())
def helper_parse(pool, entrypoints): # Parts of the binary file should be parsed from plasmasm.parse_bin import create_text_bloc job_done = set([(_.section, _.address) for _ in pool.blocs]) lab_done = set(pool.blocs) for section, address in entrypoints: label = pool.find_symbol(address=address, section=section) label.insert_bloc() create_text_bloc(label, lab_done, job_done, set())
def set_dead_reg(l, d): r, w = l.rw[0], l.rw[1] if l.CPU == 'I386' and hasattr(l, 'miasm'): from miasmX.expression import expression w = set([reg for reg in w if isinstance(reg, expression.ExprId)]) else: w = set([reg for reg in w if not str(reg) in never_dead]) l.dead = d.union(w).difference(r) pic_dead_non_collision(l)
def __init__(self, symbols, name): if not type(name) in [int, str]: raise ValueError( "Symbol name shoud be int or str, rather than %s" % type(name)) self.symbols = symbols self.name = name self.nxt = None self._cfg = None self.graph = (set(), set()) self.ender = None self.flow = None self._bloc_idx = None self.stack = Stack(self)
def set_cfg(self, cfg, graph0=None): if not type(cfg) in [tuple, list, set]: raise ValueError("CFG of %s should be a list, not %s" % (self, cfg)) self._cfg = tuple(cfg) # Cleanup the 'prv' of next nodes for label in self.graph[0]: label.graph[1].remove(self) # Reconstruct the 'prv' of next nodes if graph0 is None: graph0 = set(self.cfg).difference(set([None])) self.graph = (graph0, self.graph[1]) for label in self.graph[0]: label.graph[1].add(self)
def stack_shift(value, imm, no_copy=False): registers, positions = value if registers is False: # top return value if imm is None: # Happens when the source is C++, e.g. 'subq %rax, %rsp' return False, set() if no_copy and imm == 0: return value new_pos = set() for pos in positions: new_pos.add(pos+imm) new_reg = {} for k in registers: new_reg[k] = set([imm+_ for _ in registers[k]]) return new_reg, new_pos
def stack_tracking(label, value): # Analyzes the use of the red zone. # Cf. http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/ # Without this analysis, we cannot do push/pop on x64 without risking # to overwrite values in the red zone. # A value line.stack is created, which contains a pair: # - registers that contain (a shifted value of) the base address of the # red zone (one may store rsp+imm in rbp and afterwards access elements # in the red zone using an offset to rbp bigger than imm). # Usually, it is empty or { %rbp => imm } which means # that %rbp == %rsp+imm # - positions in the red zone that are used after the instruction has # been executed (multiple of 8). # Functions start with 'pushq' which means the stack can be modifed, but # at some point there is a 'subq $imm, rsp' and things may become ugly. if value is None: value = {}, set() if not hasattr(label, 'lines'): return value # Special case: # Sometimes %rsp+shift is copied in %rdi # This is used for string copy, we need to know the size of # the string (in ecx) to set many positions as being accessed str_copy = {} for line in label.lines: # Not instructions if isinstance(line, Constant): continue value = update_value(line, value, str_copy) line.stack = value return value
def add_line_update_dead(label, line, pos=None, dead_from=False): # NB: this function updates dead registers, but does not update # the CFG, even if 'line' is a jmp/jcc/ret/call, because it might # be an obscure predicate and the CFG may not correspond to a local # analysis of 'line' if isinstance(line, str): line = label.symbols.arch(label.symbols).from_txt(line) line.do_post_init() if pos is None: NEVER if dead_from == False: if pos < len(label.lines): dead = label.lines[pos].dead else: nxt_bloc = get_dst_bloc(label) if nxt_bloc is None: dead = set() else: dead = get_dead_bloc(nxt_bloc) set_dead_reg(line, dead) else: line.dead = dead_from.dead.copy() label.addline(line, pos=pos) if len(label.lines) >= 3 and not label.lines[-3].flow in [ None, False ] and label.lines[-3].flow.startswith('D.'): # Line added in a delay slot label.lines[-3], label.lines[-2] = label.lines[-2], label.lines[-3] return line
def get_instr_expr(l, my_eip, args=None, segm_to_do=set()): if args is None: args = [] for x in l.arg: args.append(dict_to_Expr(x, l.m.modifs, l.opmode, l.admode, segm_to_do)) l.arg_expr = args return get_instr_expr_args(l, args, my_eip)
def __init__(self): self.symbols = [] self.symbols_byname = {} self.symbols_byaddress = {} self.blocs = [] self.bloc_set = set() self.sections = Sections() self._numeric_label_ = 0 self._precomputed = {} self.meta = {}
def stack_merge(v1, v2): if v1 == v2: return v1 if v2 is None: return v1 if v1 is None: return v2 r1, p1 = v1 r2, p2 = v2 if r1 is False or r2 is False: # top return False, set() if p2 != set(): p1 = p1.union(p2) if r1 != r2: new_reg = {} for k in r1: if k in r2: new_reg[k] = r1[k].union(r2[k]) else: new_reg[k] = r1[k] for k in r2: if not k in r1: new_reg[k] = r2[k] r1 = new_reg return r1, p1
def flatten_once(self): # Outputs the list of all known possible pairs (top, stack) stacks = self.stacks[:] marked = set() out = [] while len(stacks) > 0: top, stack = stacks.pop() if isinstance(top, StackTopVoid): if not stack in marked: marked.add(stack) for _ in stack.stacks: if not _ in stacks: stacks.append(_) else: out.append((top, stack)) return out
def get_dst_bloc(label): dst = get_dst(label) seen = set() while True: if dst is None: return None if not hasattr(dst, 'lines'): return None if len(dst.lines) > 0: return dst if dst in seen: # This can happen, e.g. when a bloc # label: # jmp label # is modified by 'replace_jmp_pushret' # because temporarily it becomes an empty bloc # in a loop with itself return None seen.add(dst) dst = get_dst(dst)
def pic_dead_non_collision(l): if not 'PIC' in l.symbols.get_meta(): return if not hasattr(l, 'pic'): return # For PIC code, even if the PIC register is written, # don't consider it as dead # The other possibility would have been to modify the PIC # register list when the register is dead, but this option # is not chosen because we need PIC registers for many # obfuscation primitives pic = set() for reg in l.pic[1]: if 'ebp' in reg: pic.add('ebp') if 'esp' in reg: pic.add('esp') else: pic.add(reg) for d_reg in list(l.dead): if str(d_reg) in pic: l.dead.discard(d_reg)
def flatten_recursive(self, marked=None): # Outputs the list of all known possible stacks if marked is None: marked = set() if self in marked: return [] out = [] marked.add(self) for pos, stk in sorted(self.stacks, key=lambda _: str(_[0])): stk = stk.flatten_recursive(marked) if stk == []: if not isinstance(pos, StackTopVoid): out.append([pos]) else: if isinstance(pos, StackTopVoid): pos = [] else: pos = [pos] for s in stk: out.append(pos + s) return out
def set_dead_bloc(label, marked_blocs): if label in marked_blocs: if marked_blocs[label] == 2: return marked_blocs[label] += 1 else: marked_blocs[label] = 1 # Don't compute dead registers for data blocs or empty blocs if len(label.lines) == 0: return if getattr(label.lines[-1], 'flow', False) == False: return if getattr(label.lines[0], 'flow', False) == False: return # If the flow can continue to only one other line, then we compute # its dead registers nxt_bloc = get_dst_bloc(label) if nxt_bloc in label.symbols.bloc_set: set_dead_bloc(nxt_bloc, marked_blocs) d = get_dead_bloc(nxt_bloc) if d is None: d = set() # Update dead registers in the bloc lines = list(reversed(label.lines)) if not label.flow in [None, False] and label.flow.startswith('D.'): # Delayed slot => swap the last lines lines[0], lines[1] = lines[1], lines[0] # We may want to test if it is a SPARC cpu # Annul bit => delay slot not executed unless conditional branch taken if label.flow in ['D.jmp', 'D.jcc']: set_dead_reg(lines[0], d) d = lines[0].dead set_dead_reg(lines[1], d) lines[0:2] = [] for idx, l in enumerate(lines): set_dead_reg(l, d) set_dead_PIC(lines, idx) d = l.dead
def propagate_cfg(symbols, analysis): # Per object, propagatation by applying 'analysis' once to each basic # bloc, following the CFG. def follow_cfg(label, done): todo = [ (label, None) ] while len(todo): label, value = todo.pop(0) if label in done: continue done.add(label) value = analysis(label, value) if label.flow in ['sub', 'D.sub']: # Don't follow function calls todo.append((label.nxt, value)) else: todo.extend([(_, value) for _ in label.cfg]) return done done = set([None]) # Only once per basic bloc for o in symbols.object_list(): follow_cfg(o[0], done) # Split in objects may be erroneous, local functions may be forgotten # cf. trees.s from pari 2.5.5 / clang 700 64-bit for label in symbols.blocs: if not label in done: follow_cfg(label, done)
# Copyright (C) 2011-2020 Airbus, [email protected] containers = { 'ELF': 'SPARC' } try: from plasmasm.python.compatibility import set except ImportError: pass opcodes = { 'SPARC': set(['add', 'addcc', 'addx', 'and', 'andcc', 'b', 'ba', 'be', 'be,a', 'bg', 'bge', 'bgu', 'bl', 'ble', 'ble,a', 'blu', 'bne', 'bne,a', 'bset', 'call', 'clr', 'cmp', 'dec', 'inc', 'jmp', 'ld', 'ldd', 'ldsb', 'ldub', 'lduh', 'mov', 'nop', 'or', 'restore', 'retl', 'save', 'sdiv', 'sethi', 'sll', 'smul', 'sra', 'srl', 'st', 'stb', 'std', 'sub', 'subcc', 'udiv', 'wr', 'xor']), }
def get_r(self, mem_read=False): r = set() for a in self.args: r = r.union(a[0].get_r(mem_read)) return r
def labels(self): ''' labels that are referenced in the line ''' return set([v for v in self.value if hasattr(v, 'name')])
'fpatan', 'fprem', 'fprem1', 'fptan', 'fwait', 'fxam', 'fxch', 'fxrstor', 'fxsave', 'in', 'jmp', 'lahf', 'ldmxcsr', 'leave', 'lfence', 'lock', 'lodsw', 'mfence', 'nop', 'out', 'rep', 'ret', 'sahf', 'scasb', 'scasw', 'sfence', 'stc', 'std', 'sti', 'stmxcsr', 'stosb', 'stosl', 'stosw', 'ud2', 'wait', 'xrstor', 'xsave', 'xsaveopt', ] opcodes = { 'I386-intel': set(MMX + NOSUFFIX + TEST + ['.p2align', 'adc', 'add', 'and', 'bsf', 'bsr', 'bswap', 'bt', 'call', 'cbw', 'cdq', 'cmc', 'cmp', 'cmpsw', 'cwd', 'cwde', 'dec', 'div', 'fadd', 'faddp', 'fcmovb', 'fcmovbe', 'fcmove', 'fcmovnb', 'fcmovnbe', 'fcmovne', 'fcmovnp', 'fcmovnu', 'fcmovu', 'fdiv', 'fdivp', 'fdivr', 'fdivrp', 'fiadd', 'fidiv', 'fidivr', 'fild', 'fimul', 'fist', 'fistp', 'fisttp', 'fisub', 'fisubr', 'fmul', 'fmulp', 'fsqrt', 'fst', 'fstp', 'fsub', 'fsubp', 'fsubr', 'fsubrp', 'fucom', 'fucomi', 'fucomip', 'fucomp', 'fucompp', 'idiv', 'imul', 'inc', 'lea', 'mov', 'movsw', 'movsx', 'movzx', 'mul', 'neg', 'not', 'or', 'pop', 'popfd', 'push', 'pushfd', 'repz', 'repnz', 'rol', 'ror', 'sal', 'sar', 'sbb', 'shl', 'shld', 'shr', 'shrd', 'sub', 'test', 'xadd', 'xchg', 'xor']), 'I386-att': set(MMX + NOSUFFIX + TEST + TEST_att + ['cvtsi2sdl', 'cvtsi2ssl', '.p2align', 'adcl', 'addb', 'addl', 'addw', 'andb', 'andl', 'andw', 'bsfl', 'bsrl', 'bswap', 'bswapl', 'btl', 'btsl', 'call', 'calll', 'cbtw', 'cltd', 'cmpb', 'cmpl', 'cmpsb', 'cmpsw', 'cmpw', 'cwtd', 'cwtl', 'decb', 'decl', 'decw', 'divb', 'divl', 'divw', 'fadd', 'faddl', 'faddp', 'fadds', 'fcmovb', 'fcmovbe', 'fcmove', 'fcmovnb', 'fcmovnbe', 'fcmovne', 'fcmovnu', 'fcmovu', 'fcom', 'fcomi', 'fcomip', 'fcomp', 'fcompp', 'fdiv', 'fdivl', 'fdivp', 'fdivr', 'fdivrl', 'fdivrp', 'fdivrs', 'fdivs', 'fiaddl', 'fidivl', 'fidivrl', 'fildl', 'fildll', 'fildq', 'filds', 'fimull', 'fistps', 'fistl', 'fistpl', 'fistpll', 'fistpq', 'fisttpl', 'fisttpll', 'fisttpq', 'fisubl', 'fisubrl', 'fldl', 'flds', 'fldt', 'fmul', 'fmull', 'fmulp', 'fmuls', 'fsqrt', 'fstl', 'fstp', 'fstpl', 'fstps', 'fstpt', 'fsts', 'fsub', 'fsubl', 'fsubp', 'fsubr', 'fsubrl', 'fsubrp', 'fsubrs', 'fsubs', 'fucom', 'fucomi', 'fucomip', 'fucomp', 'fucompi', 'fucompp', 'idivb', 'idivl', 'imull', 'imulw', 'incb', 'incl', 'incw', 'insb', 'insd', 'insw', 'jmpl', 'leal', 'lgdtl', 'ljmp', 'movb', 'movl', 'movsb', 'movsbl', 'movsbw', 'movsl', 'movsw', 'movswl', 'movw', 'movzbl', 'movzbw', 'movzwl', 'mulb', 'mull', 'negb', 'negl', 'notb', 'notl', 'notw', 'orb', 'orl', 'orw', 'outsb', 'outsd', 'outsw', 'popfl', 'popl', 'pushfl', 'pushl', 'pushw', 'repnz', 'repz', 'retl', 'rolb', 'roll', 'rolw', 'rorb', 'rorl', 'rorw',
def get_expr_ids(e): ids = set() e.visit(lambda x: get_expr_ids_visit(x, ids)) return ids
def guess_asm_cpu(symbols): from plasmasm import arch cpu_opcodes = {} for cpu in arch.CPUs: cpu_opcodes.update(arch.import_cpu_meta(cpu).opcodes) opcodes = set() operands = set() for label in symbols.symbols: for line in getattr(label, 'lines', []): if not isinstance(line, Instruction): continue opcodes.add(line.opname) operands.update(line.operands) if len(operands) == 0: return None delta = [] for cpu in cpu_opcodes: delta.append((len(opcodes.difference(cpu_opcodes[cpu])), cpu)) delta.sort() cpu = delta[0][1] if delta[0][0] == 0 and delta[1][0] != 0: return cpu if delta[0][0] == delta[1][0]: val = delta[0][0] cpu = [x for n, x in delta if n == val] # Filter possible cpu by looking at operands # Necessary to make the difference between I386 and X64 cpu_base = set([_ for _ in cpu if not '-' in _]) cpu_base.update(set([_[:_.find('-')] for _ in cpu if '-' in _])) for op in operands: if op in ('%g0', '%g1', '%o0'): NON_REGRESSION_FOUND cpu_base.intersection_update(set(['SPARC'])) for reg in [ 'rax', 'rbx', 'rcx', 'rdx', 'rbp', 'rsp', 'rsi', 'rdi', 'rip' ] + ['r%d' % _ for _ in range(8, 16)]: if op in (reg, '%' + reg, '(%' + reg + ')'): cpu_base.intersection_update(set(['X64'])) if '%' + reg in op or '[' + reg in op: cpu_base.intersection_update(set(['X64'])) if op in ('eax', '%eax'): cpu_base.intersection_update(set(['X64', 'I386'])) if len(cpu_base) <= 1: break if cpu_base == set(['I386', 'X64']): # Sometimes an assembly file generated for 64-bit architecture # is also a valid 32-bit assembly; but we need to know which # was the target architecture, e.g. obfuscation will be different if '.rodata.str1.8' in symbols.sections.asm_name: # 32-bit uses .rodata.str1.4 NON_REGRESSION_FOUND cpu_base = set(['X64']) else: cpu_base = set(['I386']) # Get back to full CPU description (with AT&T/Intel syntax) def startswith(cpu, cpu_base): # str.startswith(tuple) is invalid for python 2.3 for _ in cpu_base: if cpu.startswith(_): return True return False cpu = [_ for _ in cpu if startswith(_, cpu_base)] if len(cpu) == 1: return cpu[0] if set(cpu) == set(['I386-att', 'I386-intel']): if not 'format' in symbols.get_meta(): return 'I386-att' else: NON_REGRESSION_FOUND return 'I386-intel' log.warning("Guessing cpu: %s are matching", cpu) return cpu[0] diff = opcodes.difference(cpu_opcodes[cpu]) if len(diff) * 2 > len(opcodes): log.warning( "Guessing cpu: best guess is %s but too many opcodes are missing", cpu) return None log.warning("Guessing cpu %s; additional opcodes are %s", cpu, sorted(list(diff))) return cpu
def compute_dst(self): if not hasattr(self, 'lines') or not None in self.cfg: return None if not section_type(self.section) in ['text', 'plt']: return None if len(self.lines) == 0: return None idx = -1 if len(self.lines) > 1 and self.lines[-2].flow is not None: idx = -2 instr = self.lines[idx] if instr.flow == False: return None if instr.flow.endswith('ret'): return None log.debug("dst of '%s' at (%s)", self, instr) if hasattr(instr, 'dst') and \ len(instr.dst) >= 1 and \ isinstance(instr.dst[0], list): # Non-resolved switch table table = instr.dst[0][0] if not hasattr(table, 'lines'): log.debug("SWITCH TABLE AT LABEL %s, empty", table) return None if not hasattr(table, 'switch_table'): log.debug("SWITCH TABLE AT LABEL %s, %d lines, no base", table, len(table.lines)) instr.dst = [] for line in table.lines: if not hasattr(line, 'value'): assert line.__class__.__name__ == 'ConstantZero' break if not isinstance(line.value[0], Symbol): break instr.dst.append(line.value[0]) return instr switch_base, ptr_size, tbl_size = table.switch_table if hasattr(table, 'size'): log.debug("SWITCH TABLE AT LABEL %s, %d lines, complete", table, len(table.lines)) instr.dst = [] base = "-%s" % switch_base for line in table.lines: label = line.value[0] assert label.name.endswith(base) label = label.name[:-len(base)] label = self.symbols.find_symbol(name=label) instr.dst.append(label) return instr log.debug("SWITCH TABLE AT LABEL %s, %d lines, incomplete", table, len(table.lines)) if tbl_size is not None: log.debug(" tbl_size=%s", tbl_size) return None in_str, _ = self.symbols.streams[self.section] # Note that the performance impact is HUGE # e.g. parsing examples/x64_linux/k9-fPIC.o takes 39s instead of 3s # TODO: recompute only if the ancestors of 'self' have changed # or if the target switch table has changed # This should gain a lot of time lines = self.lines # May be extended with previous blocs msg, val = instr.evaluate_lines(lines, in_str) cur_bloc = self while val == [None]: # If the analysis has not been conclusive, and if there is # only one ancestor bloc, then we emulate it too ancestors = cur_bloc.get_ancestors() log.debug("Ancestors %s", [str(_) for _ in ancestors]) if len(ancestors) != 1: break cur_bloc = ancestors[0] if not hasattr(cur_bloc, 'lines'): break lines = cur_bloc.lines + lines msg, val = instr.evaluate_lines(lines, in_str) self.del_data('cfg_warn') # Warning displayed only at the end if val == [None]: msg, retval, machine = msg self.set_data('cfg_warn', (instr, msg, retval)) log.debug("Failed to compute dst of '%s' at (%s)", self, instr) log.debug("... %s (TODO) '%s'", msg, retval) log.debug(" MEMORY is") for mem in machine: log.debug(" - %s", mem) elif val == 'TABLE': # Table found, but data section not parsed yet log.debug("... %s %s", msg, val) val = [None] elif len(val) == 1: log.debug("... %s '%s'", msg, val[0]) else: log.debug("... %s %s", msg, [str(v) for v in val]) if val == instr.dst: # Nothing changed return None if len(val) >= len(instr.dst): # New destinations found # Note that 'None' in the destination list means that the # analysis is incomplete instr.dst = val return instr if set(val + [None]) == set(instr.dst): # Destination list completed # e.g. when the end of a switch table has been found # Recompute CFG to delete None instr.dst = val return instr if set(val) < set(instr.dst): # In some rare cases, compute_dst generated an element in a # switch table, which changes the CFG before this instruction, # and makes our backward analysis fail because we go backwards # only if a basic has a unique ancestor. return None # Should not happen, it means that the new destination list # is different from the old one in a strange way log.error("Bloc %r", self) log.error(" OLD DST %s", instr.dst) log.error(" NEW DST %s", val) TODO
def get_w(self): return set([self]) #[memreg]
def update_value(line, value, str_copy): if isinstance(line, P2Align): return value if line.api_nb_arg() == 2: src = line.api_arg_txt(1, asm_format='att_syntax') dst = line.api_arg_txt(0, asm_format='att_syntax') # Top if value == (False, set()): return value # Analysis not possible if '(%rsp,' in str(line): return False, set() # When the stack moves... imm = line_shifts_stack(line) if imm != 0: if imm is not None: imm = -imm return stack_shift(value, imm) # Sometimes %rsp is copied in another register if 'mov' == line.opname \ and src == '%rsp': reg, val = value reg = dict(reg) reg[dst] = set([0]) return reg, val # Sometimes %rsp is copied in another register, with an offset if 'lea' == line.opname \ and src.endswith('(%rsp)'): imm = src[:-6] if imm == '': imm = 0 else: imm = int(imm) reg, val = value reg = dict(reg) reg[dst] = set([imm]) return reg, val # Sometimes the other register is shifted if 'add' == line.opname \ and dst in value[0]: imm = line.api_get_cst(1) if imm is None: return value else: registers = dict(value[0]) registers[dst] = set([_ + imm for _ in registers[dst]]) return registers, value[1] # String copy into the red zone if 'mov' == line.opname \ and src.startswith('$') \ and dst in ('%ecx', '%cl'): imm = line.api_get_cst(1) if imm is not None: str_copy['ecx'] = imm return value if line.opname in ('movsq', 'stosq', 'scasq') \ and line.prefix == [243]: # rep if 'ecx' in str_copy and '%rdi' in value[0]: imm = value[0]['%rdi'] assert len(imm) == 1 imm = list(imm)[0] positions = value[1].union(set( [imm+8*idx for idx in range(str_copy['ecx'])])) return value[0], positions else: return value # Use of the red zone starting from %rsp if line.opname in ('mov', 'movss', 'movsd', 'movaps', 'movups', 'movlps', 'movdqa', 'movdqu', ) \ and dst.endswith('(%rsp)'): imm = dst[:-6] if imm == '': imm = 0 else: imm = int(imm) positions = value[1].union(set([imm])) if line.opname in ('movaps', 'movups', 'movlps', 'movdqa', 'movdqu', ): # input is 128-bit long # note that movss %xmmN, MEM only moves 32 bits positions.update([imm+8]) return value[0], positions # Use of the red zone starting from another register if line.opname in ('mov', 'movsxd') \ and dst.endswith(')'): reg = dst[-5:-1] if reg in value[0]: # registers imm = dst[:-6] if imm == '': imm = 0 else: imm = int(imm) positions = value[1].union(set([imm+_ for _ in value[0][reg]])) return value[0], positions else: return value # Sometimes the other register is overwritten if set(value[0].keys()).intersection(set(['%%%s'%_ for _ in line.rw[1]])): registers = dict(value[0]) written = set(['%%%s'%_ for _ in line.rw[1]]) for _ in value[0]: if _ in written: del registers[_] return registers, value[1] return value
def labels(self): ''' labels that are referenced in the line ''' return set()
x64_att_opcodes = set([ 'jmpq', 'callq', 'retq', 'popq', 'pushq', 'movq', 'cmpq', 'testq', 'leaq', 'btq', 'bswapq', 'notq', 'orq', 'xorq', 'andq', 'bsfq', 'bslq', 'bsrq', 'rolq', 'rorq', 'sarq', 'salq', 'shrq', 'shlq', 'sbbq', 'negq', 'decq', 'incq', 'adcq', 'addq', 'subq', 'mulq', 'divq', 'imulq', 'idivq', 'shldq', 'shrdq', 'cltq', 'cqto', 'movabsq', 'movsbq', 'movslq', 'movswq', 'insq', 'movsq', 'outsq', 'lodsq', 'stosq', 'cmpsq', 'scasq', 'pextrq', 'pinsrq', 'cvtsi2sdq', 'cvtsi2ssq', 'cvttsd2siq', 'cvttss2siq', ])
def get_r(self, mem_read=False): if mem_read: return set(self.arg.get_r(mem_read).union(set([self]))) else: return set([self])