def getInitialConditionsAlloc(): ret_op = Operand("eax", "DWORD") ret_val = 0 initial_values_at_alloc = dict() initial_values_at_alloc[ret_op] = Operand(str(ret_val), "DWORD") return initial_values_at_alloc
def fixMemoryAccess(self, mem_access): assert (mem_access <> None) #print self.instruction if (self.instruction == "ldm"): self.write_operands = [self.operands[2]] self.mem_reg = self.operands[0] #if (mem_regs): # self.read_operands = [self.operands[0]] mem_source = mem_access["source"] mem_offset = mem_access["offset"] for i in range(self.operands[2].size): name = mem_source + "@" + str(mem_offset + i) self.read_operands.append( Operand(name, "BYTE", mem_source, mem_offset + i)) #self.read_operands.append(self.operands[0]) # stm: [op_2] = op_0 elif (self.instruction == "stm"): self.read_operands.append(self.operands[0]) self.mem_reg = self.operands[2] mem_source = mem_access["source"] mem_offset = mem_access["offset"] for i in range(self.operands[0].size): name = mem_source + "@" + str(mem_offset + i) self.write_operands.append( Operand(name, "BYTE", mem_source, mem_offset + i)) else: assert (False)
def __init__(self, pbase=None, pars=None): self.internal_size = 256 + 4 if (type(pbase) <> type(None)): self.pbase = pbase for (ptype, size, disp, needed) in self.parameter_typs: self.parameter_locs.append( (ptype, self.__locateParameter__(disp, size), needed)) else: self.__loadParameters__(pars) # populate read operands self.read_operands.append(self.parameter_locs[0]) self.read_operands.append(self.parameter_locs[1]) for i in range(self.internal_size): msrc = self.parameter_vals[1].mem_source if ("arg[" in msrc): #it's an argument! self.read_operands.append( Operand(msrc + ":" + str(i), "BYTE")) else: assert (0) #self.read_operands.append(Operand("i:"+str(i), "BYTE")) # populate write operands for i in range(self.internal_size): mem_source = self.parameter_vals[0].mem_source mem_offset = self.parameter_vals[0].mem_offset + i name = mem_source + "@" + str(mem_offset) self.write_operands.append( Operand(name, "BYTE", mem_source, mem_offset))
def parse_inputs(inputs): r = dict() for s in inputs: #print s s = s.strip("(").strip(")") (rop, rval) = s.split(",") ropsize, rop = rop.split(" ") rvalsize, rval = rval.split(" ") mem_source = None mem_offset = None if ('arg[' in rop): pass elif ('@' in rop): mem_source, mem_offset = rop.split('@') mem_offset = int(mem_offset) if (ropsize == rvalsize and rvalsize == "VAR"): for i, c in enumerate(rval): # TODO: this is only for inputs! r[Operand(rop + str(i), "BYTE", mem_source, mem_offset)] = Operand(str(ord(c)), "BYTE") else: r[Operand(rop, ropsize, mem_source, mem_offset)] = Operand(rval, rvalsize) return r
def __getESPdifference__(self, reil_code, initial_esp): #print "inf:", reil_code.first, reil_code.last if len(reil_code) == 0: return initial_esp esp_op = Operand("esp", "DWORD") initial_values = dict([(esp_op, Operand(str(0), "DWORD"))]) return getValueFromCode(reil_code, initial_values, esp_op) + initial_esp
def mkVal(val_type, val): if val_type == "imm": return Operand(str(val), "") elif "s." in val_type or "h." in val_type or "arg" in val_type: return Operand(val_type + "@" + str(val), "", mem_source=val_type, mem_offset=val) else: assert (0)
def detectType(mvars, ins, counter, callstack): if (len(mvars) == 0): return None # dection of parameters of main name = "s." + hex(callstack.callstack[1]) + ".1" # argv argv_bytes = [] for i in range(12, 16): argv_bytes.append(Operand(name + "@" + str(i), "BYTE")) argv_bytes = set(argv_bytes) if argv_bytes.issubset(mvars): return "argv[]" ## argc #argc_bytes = [] # #for i in range(8,12): # argc_bytes.append(Operand(name+"@"+str(i),"BYTE")) # #argc_bytes = set(argv_bytes) # #if argc_bytes.issubset(mvars): # return "argc" # argv[0], argv[1], ... argv[10] for i in range(0, 40, 4): op = Operand("argv[]@" + str(i), "BYTE") if op in mvars: return "arg[" + str(i / 4) + "]" if ins.instruction == "call" and ins.called_function == "malloc": # heap pointers if set([Operand("eax", "DWORD")]).issubset(mvars): return "h." + "0x" + ins.address + "." + str(counter) elif ins.instruction == "call" and ins.called_function == None: # stack pointers if mvars.issubset( set([Operand("esp", "DWORD"), Operand("ebp", "DWORD")])): return "s." + hex(callstack.currentCall()) + "." + str( callstack.currentCounter()) # No type deduced return None
def __init__(self, raw_ins, mem_regs=True): pins = reil.parseString(raw_ins) self.address = pins.address self.instruction = pins.instruction self.operands = [] # for memory instructions self.mem_reg = None # for call instructions self.called_function = None aopers = pins.augmented_operands for (i, x) in enumerate(aopers): if x == ",": self.operands.append(Operand(aopers[i - 1], aopers[i - 2])) self.operands.append(Operand(aopers[-1], aopers[-2])) self.read_operands = [] self.write_operands = [] # ldm: op_2 = [op_0] if (pins.instruction == "ldm"): self.write_operands = [self.operands[2]] self.mem_reg = self.operands[0] # stm: [op_2] = op_0 elif (pins.instruction == "stm"): self.read_operands.append(self.operands[0]) self.mem_reg = self.operands[2] elif (pins.instruction == "jcc"): self.read_operands = filter(lambda o: not o.isEmpty(), self.operands[0:2]) self.write_operands = [] elif (pins.instruction == "call"): #print "n:", self.operands[0].name #print self.operands[0].name if (self.operands[0].name <> "EMPTY"): self.called_function = self.operands[0].name else: self.read_operands = filter(lambda o: not o.isEmpty(), self.operands[0:2]) self.write_operands = filter(lambda o: not o.isEmpty(), self.operands[2:3])
def __init__(self, pbase=None, pars=None): self.internal_size = 84 if (type(pbase) <> type(None)): self.pbase = pbase for (ptype, size, disp, needed) in self.parameter_typs: self.parameter_locs.append( (ptype, self.__locateParameter__(disp, size), needed)) else: self.__loadParameters__(pars) # populate read operands self.read_operands.append(self.parameter_locs[0]) # it is not necesary, since these locations won't be modified! #for i in range(self.internal_size): # self.read_operands.append(Operand("stdin:"+str(i), "BYTE")) # populate write operands for i in range(self.internal_size): mem_source = self.parameter_vals[0].mem_source mem_offset = self.parameter_vals[0].mem_offset + i name = mem_source + "@" + str(mem_offset) self.write_operands.append( Operand(name, "BYTE", mem_source, mem_offset))
def detectFuncParameters(self, reil_code, memaccess, callstack, inputs, counter): pins = parse_reil(reil_code[-1]) ins = Instruction(pins, None) assert (ins.instruction == "call" and ins.called_function <> None) # first we locate the stack pointer to know where the parameters are located esp = Operand("esp", "DWORD") pbase = getTypedValueFromCode(reil_code, callstack, inputs, memaccess, esp) #print pbase.name #print pbase.mem_source # func_cons = funcs.get(ins.called_function, Function) func = func_cons(pbase=pbase) parameters = [] for (par_type, location, needed) in func.getParameterLocations(): #print (ins.called_function, par_type, location.mem_source, needed) if needed: reil_code.reverse() reil_code.reset() val = getTypedValueFromCode(reil_code, callstack, inputs, memaccess, location) #print "parameter of",ins.called_function, "at", str(location) , "has value:", val.name parameters.append((location, val)) else: parameters.append((None, None)) if parameters <> []: self.parameters[counter] = self.__getParameters__(ins, parameters)
def disassemble(self, filename): #Exécution d'opdis et récupération du XML xml = subprocess.Popen( ["opdis", "-q", "-d", "-f", "xml", "-N", "main", filename], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.read() document = parseString(xml).documentElement #Création de la liste des instructions instructions_table = [] vma_instructions_table = {} for instructionNode in document.getElementsByTagName("instruction"): instruction = Instruction.Instruction( self.get_xml_child_value(instructionNode, "offset"), self.get_xml_child_value(instructionNode, "vma"), self.get_xml_child_value(instructionNode, "ascii"), self.get_xml_child_value(instructionNode, "mnemonic")) if (self.xml_node_has_child(instructionNode, "operands")): for operandNode in instructionNode.getElementsByTagName( "operand"): operand = Operand.Operand( operandNode.attributes["name"] if ("name" in operandNode.attributes) else "", self.get_xml_child_value(operandNode, "ascii")) instruction.add_operand(operand) instructions_table.append(instruction) vma_instructions_table[int(instruction.vma, 0)] = instruction return (instructions_table, vma_instructions_table)
def getEq(self, mvars): r = [] #print self.src, self.dst if (self.src.isReg()): src = self.src.name # self.src.size = self.size # srcs = self.getOperands([self.src]) # print srcs else: assert (0) old_sname, new_sname, offset = Memvars.write(self.dst) old_array = mkArray(old_sname) array = mkArray(new_sname) for i in range(self.size): dst_op = Operand(self.dst.mem_source + "@" + str(offset + i), "BYTE") src_var = z3.BitVec(src + ":" + str(i) + "(0)", 8) if (dst_op in mvars): array = z3.Store(array, offset + i, src_var) r.append(src_var <> 0) r.append((old_array == array)) return r
def __locateParameter__(self, disp, size): #assert(disp<=0) mem_source = self.pbase.mem_source mem_offset = self.pbase.mem_offset + disp name = mem_source + "@" + str(mem_offset) return Operand(name, size, mem_source, mem_offset)
def addAditionalConditions(mvars, ins, ssa, callstack, smt_conds): if len(mvars) == 0: return mvars # auxiliary eq condition eq = Eq(None, None) # if the instruction was a call if ins.instruction == "call" and ins.called_function == "malloc": if (Operand("eax", "DWORD") in mvars): initial_values_at_alloc = getInitialConditionsAlloc() setInitialConditions(ssa, initial_values_at_alloc, smt_conds) mvars.remove(Operand("eax", "DWORD")) elif ins.instruction == "call" and ins.called_function == None: initial_values_at_call = getInitialConditionsCall(callstack) for iop in initial_values_at_call.keys(): #print "iop:",iop if not (iop in mvars): del initial_values_at_call[iop] setInitialConditions(ssa, initial_values_at_call, smt_conds) mvars = set( filter(lambda o: not (o in initial_values_at_call.keys()), mvars)) new_mvars = set() for v in mvars: # we convert stack memory variables from one frame to the previous one if callstack.currentCounter() > 1 and v.isStackMem( ) and v.mem_offset >= 4: eop = callstack.convertStackMemOp(v) smt_conds.add(eq.getEq(v, eop)) new_mvars.add(eop) else: new_mvars.add(v) mvars = set( filter(lambda o: not (o.isStackMem() and o.mem_offset >= 4), mvars)) mvars = mvars.union(new_mvars) return mvars
def decode(self): #self.inst = inst self.op = self.bits(0, 0xF0000000) # Large embedded operand if len(self.inst) == 2 and self.bits(1, 3) == 3: self.immediate = True else: self.immediate = False # Flow control instruction if self.bits(0, 2): self.system = True else: self.system = False # Is a second, full word present? self.fullinst = (len(self.inst) == 2) and not self.immediate # Changes some instructions # Seems to choose an alternative instruction set, for example, # add becomes sub. It is also used on mul24.lo.s32 sometimes # but I have no idea what its effect is there. # inst.alt = (inst.flags & 1) # Predication if self.fullinst: # Predicated execution self.pred_op = self.bits(1, 0x00000F80) self.pred = self.bits(1, 0x00003000) # Predication (set) # This should be an operand if self.bits(1, 0x0000040): self.dst_operands.append( Operand(OP_TYPE_PRED, OP_SIGN_NONE, 1, OP_SOURCE_PRED_REGISTER, OP_INDIRECTION_NONE, self.bits(1, 0x00000030))) else: self.pred_op = None # Don't get in the way # Ignore out #if self.fullinst: # self.output_reg = self.bits(1,0x0000008) #else: # self.output_reg = False if self.bits(0, 3) == 2: self.warnings.append("Unknown marker 0.2") if self.fullinst and self.bits(1, 3) == 2: #self.warnings.append("Unknown marker 1.2") # join point? self.modifiers.append(".join") if self.fullinst and self.bits(1, 3) == 1: self.modifiers.append(".end") if len(self.inst) == 1: self.modifiers.append(".half")
def convertStackMemOp(self, op): #print str(op) self.index = self.index - 1 mem_source = "s." + hex(self.currentCall()) + "." + str( self.currentCounter()) if self.index == 1: mem_offset = (op.mem_offset) + self.currentStackDiff() - 4 #+16 else: mem_offset = (op.mem_offset) + self.currentStackDiff() #+16 name = mem_source + "@" + str(mem_offset) self.index = self.index + 1 return Operand(name, "BYTE", mem_source, mem_offset)
def decode(self): super(ldofs0, self).decode() src = self.offset_reg() if src: # We can add something to an offset register by # providing an offset register as source for this instruction self.base = "add" else: self.base = "mov" type = (OP_SIGN_NONE, 32, OP_TYPE_INT) self.dst_operands.append(self.decode_operand(type, 2, doffset=True)) if src: self.src_operands.append( Operand(OP_TYPE_INT, OP_SIGN_NONE, 32, OP_SOURCE_OFFSET_REGISTER, OP_INDIRECTION_NONE, src)) self.src_operands.append(self.decode_operand(type, 5))
def __init__(self, pbase=None, pars=None): self.internal_size = 10 if (type(pbase) <> type(None)): self.pbase = pbase for (ptype, size, disp, needed) in self.parameter_typs: self.parameter_locs.append( (ptype, self.__locateParameter__(disp, size), needed)) else: self.__loadParameters__(pars) # populate read operands self.read_operands.append(self.parameter_locs[0]) # return value self.write_operands.append(Operand("eax", "DWORD"))
def getInitialConditionsCall(callstack): initial_values_at_call = dict() if callstack.index == 1: esp_val = 4 for i in range(0, 40): arg_i = Operand("argv[]@" + str(i), "BYTE", mem_source="argv[]", mem_offset=i) initial_values_at_call[arg_i] = Operand(str(0), "BYTE") name = "s." + hex(callstack.callstack[1]) + ".1" for i in range(12, 16): arg_v = Operand(name + "@" + str(i), "BYTE", mem_source=name, mem_offset=i) initial_values_at_call[arg_v] = Operand(str(0), "BYTE") #print "" else: esp_val = 8 ebp_val = 0 esp_op = Operand("esp", "DWORD") ebp_op = Operand("ebp", "DWORD") initial_values_at_call[esp_op] = Operand(str(esp_val), "DWORD") initial_values_at_call[ebp_op] = Operand(str(ebp_val), "DWORD") return initial_values_at_call
def getEq(self, mvars): r = [] old_sname, new_sname, offset = Memvars.write(self.dst) old_array = mkArray(old_sname) array = mkArray(new_sname) for i in range(self.size): op = Operand(self.dst.mem_source + "@" + str(offset + i), "BYTE") if (op in mvars): array = z3.Store(array, offset + i, z3.BitVec("stdin:" + str(i) + "(0)", 8)) r.append(z3.BitVec("stdin:" + str(i) + "(0)", 8) <> 10) r.append(z3.BitVec("stdin:" + str(i) + "(0)", 8) <> 0) r.append((old_array == array)) return r
def __init__(self, regs, opc): self.opcode = opc self.operations = [] self.operands = [] self.valid = False opc_cmdname = opc.getDisasm().split(' ')[0].lower() opc_cmdtype = opc.getCmdType() #Populate operands for i in range(0, 3): oprnd = None opc_oprnd = opc.operand[i] otype = opc_oprnd[0] osize = opc_oprnd[1] if (otype & DECR_ISREG): self.operands.append( Operand(OPRND_REG, osize, [IMMREG_MAP[osize][opc_oprnd[2].index(1)]])) elif (otype & DEC_CONST): self.operands.append( Operand(OPRND_CONST, osize, [opc_oprnd[3]])) #Special case for lea elif (otype == DEC_UNKNOWN and osize == 4): for i in range(len(opc_oprnd[2])): if (not opc_oprnd[2][i]): continue self.operands.append( Operand(OPRND_REG, osize, [IMMREG_MAP[osize][i]])) elif (otype & DEC_TYPEMASK): self.operands.append( Operand(OPRND_MEM, osize, [ IMMREG_MAP[osize][i] for i in len(opc_oprnd[2]) if opc_oprnd[2][i] ], calcaddr(opc_oprnd, regs))) #Figure out what type of operation it is and parse accordingly if (opc_cmdtype == C_CMD): if (opc_cmdname in [ 'adc', 'add', 'sub', 'sbb', 'and', 'or', 'xor', 'shld', 'shrd' ]): self.operations.append( Operation(OPRTN_ARITH, [self.operands[0], self.operands[1]], [self.operands[0]])) elif (opc_cmdname in [ 'inc', 'dec', 'not', 'neg', 'shl', 'shr', 'sal', 'sar', 'shr', 'rcl', 'rcr', 'rol', 'ror' ]): self.operations.append( Operation(OPRTN_ARITH, [self.operands[0]], [self.operands[0]])) elif (opc_cmdname in ['mov', 'movsx', 'movzx']): self.operations.append( Operation(OPRTN_OVERWRITE, [self.operands[1]], [self.operands[0]])) elif (opc_cmdname in ['lea']): self.operations.append( Operation(OPRTN_ARITH, self.operands[1:], [self.operands[0]])) elif (opc_cmdname in ['div', 'idiv', 'mul', 'imul', 'imul', 'imul']): ocount = len(self.operands) if (ocount == 1): oxsize = self.operands[0].size dest = [] if (oxsize == 1): dest.append(Operand(OPRND_REG, 2, [REG_MAP['AX']])) elif (oxsize == 2): dest += [ Operand(OPRND_REG, 2, [REG_MAP['DX']]), Operand(OPRND_REG, 2, [REG_MAP['AX']]) ] elif (oxsize == 4): dest += [ Operand(OPRND_REG, 4, [REG_MAP['EDX']]), Operand(OPRND_REG, 4, [REG_MAP['EAX']]) ] self.operations.append( Operation(OPRTN_ARITH, [ self.operands[0], Operand(OPRND_REG, oxsize, [IMMREG_MAP[oxsize][0]]) ], dest)) elif (ocount == 2): self.operations.append( Operation(OPRTN_ARITH, [self.operands[0], self.operands[1]], [self.operands[0]])) elif (ocount == 3): self.operations.append( Operation(OPRTN_ARITH, [self.operands[1], self.operands[2]], [self.operands[0]])) elif (opc_cmdname in ['cbw', 'cwd']): self.operations.append( Operation(OPRTN_ARITH, [Operand(OPRND_REG, 1, [REG_MAP['AL']])], [ Operand(OPRND_REG, 2, [REG_MAP['DX']]), Operand(OPRND_REG, 2, [REG_MAP['AX']]) ])) elif (opc_cmdname in ['cwde', 'cdq']): dest = [Operand(OPRND_REG, 4, [REG_MAP['EAX']])] if (opc_cmdname == 'cdq'): dest.append(Operand(OPRND_REG, 4, [REG_MAP['EDX']])) self.operations.append( Operation(OPRTN_ARITH, [Operand(OPRND_REG, 2, [REG_MAP['AX']])], dest)) elif (opc_cmdname in ['smsw']): self.operations.append( Operation(OPRTN_OVERWRITE, [Operand(OPRND_CLEAN, 2)], [self.operands[0]])) elif (opc_cmdname in ['xchg', 'xadd']): self.operations.append( Operation(OPRTN_OVERWRITE, [self.operands[0]], [self.operands[1]])) type = OPRTN_OVERWRITE src = [self.operands[1]] if (opc_cmdname == 'xadd'): type = OPRTN_ARITH src.append(self.operands[0]) self.operations.append(Operation(type, src, [self.operands[0]])) elif (opc_cmdname in ['cmp', 'test', 'nop']): pass else: imm.log('Not implemented!' + opc_cmdname) return elif (opc_cmdtype == C_PSH): self.operations.append( Operation( OPRTN_OVERWRITE, [self.operands[0]], [Operand(OPRND_MEM, 4, [REG_MAP['ESP']], regs['ESP'] - 4) ])) elif (opc_cmdtype == C_POP): self.operations.append( Operation( OPRTN_OVERWRITE, [Operand(OPRND_MEM, 4, [REG_MAP['ESP']], regs['ESP'])], [self.operands[0]])) elif (opc_cmdtype == C_MMX): imm.log('Not implemented!' + opc_cmdname) return elif (opc_cmdtype == C_FLT): imm.log('Not implemented!' + opc_cmdname) return elif (opc_cmdtype == C_JMP): #Don't care about this instruction type pass elif (opc_cmdtype == C_JMC): #Don't care about this instruction type pass elif (opc_cmdtype == C_CAL): #Don't care about this instruction type pass elif (opc_cmdtype == C_RET): #Don't care about this instruction type pass elif (opc_cmdtype == C_FLG): imm.log('Not implemented!' + opc_cmdname) return elif (opc_cmdtype == C_RTF): imm.log('Not implemented!' + opc_cmdname) return elif (opc_cmdtype == C_REP): imm.log('Not implemented!' + opc_cmdname) return elif (opc_cmdtype == C_PRI): imm.log('Not implemented!' + opc_cmdname) return elif (opc_cmdtype == C_SSE): imm.log('Not implemented!' + opc_cmdname) return elif (opc_cmdtype == C_NOW): imm.log('Not implemented!' + opc_cmdname) return elif (opc_cmdtype == C_BAD): imm.log('Bad op encountered') return else: imm.log('Unknown op encountered') return #If we're here, the opcode is valid self.valid = True
def decode(self): super(tex, self).decode() if self.subop == 0x0: self.base = "tex" elif self.subop == 0x3: self.base = "txq" op = self.bits(0, 0x01C00000) # Dimensionality args = 4 if op in [0, 4]: self.modifiers.append(".1d") args = 1 if op in [1, 6]: self.modifiers.append(".2d") args = 2 if op in [2, 7]: if self.bits(0, 0x08000000): self.modifiers.append(".cube") else: self.modifiers.append(".3d") args = 3 (ssign, stype) = (OP_SIGN_NONE, OP_TYPE_INT) if op in [4, 6, 7]: (ssign, stype) = (OP_SIGN_SIGNED, OP_TYPE_INT) elif op in [0, 1, 2]: (ssign, stype) = (OP_SIGN_NONE, OP_TYPE_FLOAT) if op in [3, 5]: # This one is unknown self.modifiers.append(".%i" % op) if self.fullinst and not self.bits(1, 0x00000004): self.warnings.append("unknown bit not set") # Calculate destination bitfield and source/destination registers value2 = self.bits(0, 0x000001FC) # reg id if self.fullinst: bitfield = self.bits(0, 0x06000000) | self.bits(1, 0x0000c000) else: if not (value2 & 0x40): # XXX this changed bit field self.warnings.append("not (value2 & 0x40)") bitfield = 0xF value2 &= 0x3F src_set = [value2 + x for x in xrange(0, args)] count = 0 dst_set = [] for x in xrange(0, 4): if bitfield & (1 << x): dst_set.append(value2 + count) count += 1 else: dst_set.append(-1) self.dst_operands.append( Operand(value=dst_set, source=OP_SOURCE_REGSET, type=OP_TYPE_INT, sign=OP_SIGN_NONE)) # These two are the texture value1 = self.bits(0, 0x00007E00) value3 = self.bits(0, 0x003E0000) self.src_operands.append( Operand(value=value1, source=OP_SOURCE_TEXTURE, type=OP_TYPE_TEXTURE)) if value1 != value3: self.warnings.append("value3 is %i" % value3) self.src_operands.append( Operand(value=src_set, source=OP_SOURCE_REGSET, type=stype, sign=ssign))
def parse(self, text): """Parse instruction text""" # [@<pred>] <base>[.<mod1>[.<mod2>...] <dest>[|<dest2>], <src1>, <src2>, ... text = text.strip() # Predication if text.startswith('@'): # Find and remove predicate string pred_end = text.index(" ") pred = text[1:pred_end] text = text[pred_end+1:] match = re.match("(\!?)\$p([0-9]+)\.([0-9a-z]+)", pred) if not match: raise ValueError("Invalid predicate expression %s" % pred) (neg, pred, cc) = match.groups() # [!]$<pred>.<cc> try: cc = numlookup(condition_codes_rev, cc) except KeyError: raise ValueError("Invalid predicate condition code %s" % cc) if neg == "!": # Invert predicate cc = cc ^ 0xF # XXX we should be able to leave out the condition code if we just want # to evaluate the output of a set instruction self.pred_op = cc self.pred = int(pred) else: self.pred_op = 15 # always true self.pred = 0 try: base_end = text.index(" ") ins = text[0:base_end] args = text[base_end+1:].split(",") except ValueError: ins = text args = [] mods = ins.split(".") self.base = mods[0] mods = ["."+x for x in mods[1:]] # Filter out and separate types types = [] typere = re.compile("\.([subf][0-9]+|label)") for x in mods: if typere.match(x): types.append(x) else: self.modifiers.append(x) # Parse operands try: dest = args[0] dst_args = [x.strip() for x in dest.split("|")] except IndexError: dst_args = [] src_args = [x.strip() for x in args[1:]] i = 0 for x in dst_args: op = Operand() if x.startswith("$p"): # predicates have no type specifier type = "" else: try: type = types[i] except IndexError: raise ValueError("Not enough operand types in instruction") if len(types)!=1: i += 1 op.parse(x, type) self.dst_operands.append(op) for x in src_args: op = Operand() if x.startswith("$p"): # predicates have no type specifier type = "" else: try: type = types[i] except IndexError: raise ValueError("Not enough operand types in instruction") if len(types)!=1: i += 1 op.parse(x, type) self.src_operands.append(op)
def decode_operand(self, oper_type, operid, msize_bits=True, imm=False, indir=None, reg=False, regscale=True, doffset=False, spred=False, flip=False, tex=False, opt_imm=False): """What operands do we want, and in which order operid 1 is the first source operand in word 0 2 is the destination operand (usually) 3 immediate(spans both words) or non immediate (upper part of first word) 4 subsub operation or fourth operand 5 1 and 3 merged """ (sign, size, type) = oper_type source = OP_SOURCE_REGISTER indirection = OP_INDIRECTION_NONE offset = OP_OFFSET_NONE multiplier = False offset_inc = False # Offset (integrate this into operand) #t_offset = (self.flags&0x30)>>4 t_offset = self.offset_reg() if operid == 1: # first source operand, in main word if self.fullinst: # Depending on this, source is 7 or 6 bits # seems that bit 6 of oper1 has another meaning when this is a immediate # instruction (namely "32 bit") value = self.bits(0, 0x0000FE00) else: value = self.bits(0, 0x00007E00) # Process parameter numbers in shared memory if self.fullinst and self.bits( 1, 0x00200000 ) and msize_bits: # MSIZE bits collide with these indirection = OP_INDIRECTION_SHARED source = OP_SOURCE_IMMEDIATE t = (value & 0x60) >> 5 value &= 0x1F (nsign, nsize, _) = sharedmem_types[t] if nsize != size: # XXX propagate sign self.warnings.append("shared memory operand type mismatch") if not self.fullinst and self.bits(0, 0x01000000): indirection = OP_INDIRECTION_SHARED source = OP_SOURCE_IMMEDIATE t = (value & 0x30) >> 4 value &= 0xF (nsign, nsize, _) = sharedmem_types[t] if nsize != size: # XXX propagate sign self.warnings.append("shared memory operand type mismatch") elif operid == 2: # destination operand, in main word value = self.bits(0, 0x000001FC) if self.fullinst and self.bits(1, 0x0000008): # to output register source = OP_SOURCE_OUTPUT_REGISTER elif operid == 3: # immediate(spans both words) or non immediate (upper part of first word) # Extract and print operands cflag = False if self.bits(0, 0x00800000): # operand 3 comes from constant (in segment 0) indirection = OP_INDIRECTION_CONST0 source = OP_SOURCE_IMMEDIATE #if self.fullinst and self.bits(1,0x00400000) and msize_bits: # MSIZE bits collide with these # # operand 3 or 4 comes from constant in segment 1 # indirection = OP_INDIRECTION_CONST1 # source = OP_SOURCE_IMMEDIATE # #self.warnings.append("constbit") if self.fullinst and msize_bits: indirection += self.bits(1, 0x00C00000) if not self.fullinst: indirection += self.bits(0, 0x00200000) cflag = True if self.immediate: # Immediate data value = (self.bits(1, 0x0FFFFFFC) << 6) | self.bits( 0, 0x003F0000) source = OP_SOURCE_IMMEDIATE else: if cflag: # half instruction, upper bit is segment value = self.bits(0, 0x001F0000) else: value = self.bits(0, 0x007F0000) if opt_imm and self.fullinst and self.bits(1, 0x00100000): # Operand 3 is immediate source = OP_SOURCE_IMMEDIATE elif operid == 4: # sub operation or fourth operand if not self.fullinst: raise DecodeError("No operand 4 in this instruction") #if (inst[0]&0x00800000): # # operand 3 comes from constant (in segment 0) # indirection = OP_INDIRECTION_CONST0 # source = OP_SOURCE_IMMEDIATE if self.fullinst and self.bits(0, 0x01000000): # operand 4 comes from constant (in seg 0) indirection = OP_INDIRECTION_CONST0 source = OP_SOURCE_IMMEDIATE #if self.fullinst and self.bits(1,0x00400000) and msize_bits: # MSIZE bits collide with these # # operand 3 or 4 comes from constant in segment 1 # indirection = OP_INDIRECTION_CONST1 # source = OP_SOURCE_IMMEDIATE # #self.warnings.append("constbit") if self.fullinst and msize_bits: indirection += self.bits(1, 0x03C00000) value = self.bits(1, 0x001fc000) elif operid == 5: # use as much of word 0 as possible by merging 1 and 3 source = OP_SOURCE_IMMEDIATE value = self.bits(0, 0x003FFE00) elif operid == 6: source = OP_SOURCE_OFFSET_REGISTER value = t_offset # Sometimes we need to force immediate operand if imm: source = OP_SOURCE_IMMEDIATE # Force register if reg: source = OP_SOURCE_REGISTER indirection = OP_INDIRECTION_NONE # Set some indirection based on the instruction if indir != None: indirection = indir # Offset (integrate this into operand) if indirection in [ OP_INDIRECTION_LOCAL, OP_INDIRECTION_GLOBAL, OP_INDIRECTION_CONST0, OP_INDIRECTION_CONST1, OP_INDIRECTION_SHARED ] and t_offset: # ld.offset0.b32 $r01 -> 0x10 # ld.offset1.shl.b32 $r01 -> 0x10 # ld.offset1.shl.b32 $r02 -> 0x20 source = OP_SOURCE_IMMEDIATE offset = t_offset if self.bits(0, 0x02000000): offset_inc = True # Scale registers if half if regscale and source == OP_SOURCE_REGISTER and size in [ 8, 16 ] and indirection == OP_INDIRECTION_NONE: source = OP_SOURCE_HALF_REGISTER # Dest offset register? if doffset: source = OP_SOURCE_OFFSET_REGISTER # Pred register if spred: source = OP_SOURCE_PRED_REGISTER # Texture if tex: source = OP_SOURCE_TEXTURE # Address multiplier if source == OP_SOURCE_IMMEDIATE and ( indirection == OP_INDIRECTION_SHARED or (indirection >= OP_INDIRECTION_CONST0 and indirection <= OP_INDIRECTION_CONST15)): multiplier = None # ?? if size == 8: multiplier = 1 elif size == 16: multiplier = 2 elif size == 32: multiplier = 4 if multiplier != None: value *= multiplier else: self.warnings.append("Invalid multiplier") return Operand(type, sign, size, source, indirection, value, offset, flip, offset_inc)
def __init__(self, pins, mem_access, mem_regs=True): self.address = pins.address self.instruction = pins.instruction #print pins.instruction, mem_access self.operands = [] # for memory instructions self.mem_reg = None # for call instructions self.called_function = None aopers = pins.augmented_operands for (i, x) in enumerate(aopers): if x == ",": self.operands.append(Operand(aopers[i - 1], aopers[i - 2])) self.operands.append(Operand(aopers[-1], aopers[-2])) self.read_operands = [] self.write_operands = [] # ldm: op_2 = [op_0] if (pins.instruction == "ldm"): self.write_operands = [self.operands[2]] self.mem_reg = self.operands[0] if (mem_access <> None): #if (mem_regs): # self.read_operands = [self.operands[0]] mem_source = mem_access["source"] mem_offset = mem_access["offset"] for i in range(self.operands[2].size): name = mem_source + "@" + str(mem_offset + i) self.read_operands.append( Operand(name, "BYTE", mem_source, mem_offset + i)) #self.read_operands.append(self.operands[0]) #else: # print "#WARNING: No memory access information of ldm in", self.address # stm: [op_2] = op_0 elif (pins.instruction == "stm"): self.read_operands.append(self.operands[0]) self.mem_reg = self.operands[2] if (mem_access <> None): # if (mem_regs): # self.write_operands = [self.operands[2]] mem_source = mem_access["source"] mem_offset = mem_access["offset"] for i in range(self.operands[0].size): name = mem_source + "@" + str(mem_offset + i) self.write_operands.append( Operand(name, "BYTE", mem_source, mem_offset + i)) #else: # print "#WARNING: No memory access information of stm in", self.address elif (pins.instruction == "jcc"): self.read_operands = filter(lambda o: not o.isEmpty(), self.operands[0:2]) self.write_operands = [] elif (pins.instruction == "call"): #print self.operands[0].name if (self.operands[0].name <> "EMPTY"): self.called_function = self.operands[0].name else: self.read_operands = filter(lambda o: not o.isEmpty(), self.operands[0:2]) self.write_operands = filter(lambda o: not o.isEmpty(), self.operands[2:3])
def parse(self, text): """Parse instruction text""" # [@<pred>] <base>[.<mod1>[.<mod2>...] <dest>[|<dest2>], <src1>, <src2>, ... text = text.strip() # Predication if text.startswith('@'): # Find and remove predicate string pred_end = text.index(" ") pred = text[1:pred_end] text = text[pred_end + 1:] match = re.match("(\!?)\$p([0-9]+)\.([0-9a-z]+)", pred) if not match: raise ValueError("Invalid predicate expression %s" % pred) (neg, pred, cc) = match.groups() # [!]$<pred>.<cc> try: cc = numlookup(condition_codes_rev, cc) except KeyError: raise ValueError("Invalid predicate condition code %s" % cc) if neg == "!": # Invert predicate cc = cc ^ 0xF # XXX we should be able to leave out the condition code if we just want # to evaluate the output of a set instruction self.pred_op = cc self.pred = int(pred) else: self.pred_op = 15 # always true self.pred = 0 try: base_end = text.index(" ") ins = text[0:base_end] args = text[base_end + 1:].split(",") except ValueError: ins = text args = [] mods = ins.split(".") self.base = mods[0] mods = ["." + x for x in mods[1:]] # Filter out and separate types types = [] typere = re.compile("\.([subf][0-9]+|label)") for x in mods: if typere.match(x): types.append(x) else: self.modifiers.append(x) # Parse operands try: dest = args[0] dst_args = [x.strip() for x in dest.split("|")] except IndexError: dst_args = [] src_args = [x.strip() for x in args[1:]] i = 0 for x in dst_args: op = Operand() if x.startswith("$p"): # predicates have no type specifier type = "" else: try: type = types[i] except IndexError: raise ValueError("Not enough operand types in instruction") if len(types) != 1: i += 1 op.parse(x, type) self.dst_operands.append(op) for x in src_args: op = Operand() if x.startswith("$p"): # predicates have no type specifier type = "" else: try: type = types[i] except IndexError: raise ValueError("Not enough operand types in instruction") if len(types) != 1: i += 1 op.parse(x, type) self.src_operands.append(op)
def getValueFromCode(reil_code, callstack, memory, addr_op, addr, val_op, val): assert(reil_code <> []) free_variables = [] # code should be copied and reversed inss = list(reil_code) inss.reverse() # counter is set counter = len(reil_code) tracked_stack_frame = callstack.index # especial operands in a call ssa = SSA() smt_conds = SMT() # we will track op mvars = set([addr_op, val_op]) ssa_map = ssa.getMap(mvars, set(), set()) eq = Eq(None, None) addr = Operand(str(addr), "DWORD") val = Operand(str(val), "BYTE") val.size = val_op.size smt_conds.add(eq.getEq(ssa_map[addr_op.name],addr)) smt_conds.add(eq.getEq(ssa_map[val_op.name],val)) for ins_str in inss: #print ins_str.strip("\n") pins = parse_reil(ins_str) ins = Instruction(pins, memory.getAccess(counter), mem_regs = False) ins_write_vars = set(ins.getWriteVarOperands()) ins_read_vars = set(ins.getReadVarOperands()) if pins.instruction == "jcc" or len(ins_write_vars.intersection(mvars)) > 0: #if len(ins_write_vars.intersection(mvars)) > 0: ssa_map = ssa.getMap(ins_read_vars.difference(mvars), ins_write_vars, ins_read_vars.intersection(mvars)) cons = conds.get(pins.instruction, Condition) condition = cons(ins, ssa_map) mvars = mvars.difference(ins_write_vars) mvars = ins_read_vars.union(mvars) smt_conds.add(condition.getEq()) counter = counter - 1 if len(mvars) > 0: tracked_stack_frame = callstack.index if pins.instruction == "call": if callstack.index == 1: esp_val = 4 else: esp_val = 8 ebp_val = 0 esp_op = Operand("esp","DWORD") ebp_op = Operand("ebp","DWORD") initial_values_at_call = dict() initial_values_at_call[esp_op] = Operand(str(esp_val), "DWORD") initial_values_at_call[ebp_op] = Operand(str(ebp_val), "DWORD") for iop in initial_values_at_call.keys(): if not (iop in mvars): del initial_values_at_call[iop] ssa_map = ssa.getMap(set(), set(), set(initial_values_at_call.keys())) eq = Eq(None, None) for iop in initial_values_at_call: smt_conds.add(eq.getEq(ssa_map[iop.name],initial_values_at_call[iop])) mvars = set(filter(lambda o: not (o in initial_values_at_call.keys()), mvars)) if (counter == 0 and len(mvars)>0): #cond = Initial_Cond(None, None) # #for v in mvars: # print str(v), # smt_conds.add(cond.getEq(v)) #print "are free" #print smt_conds.solver free_variables = mvars break new_mvars = set() for v in mvars: if v.isMem(): # this should work for stack memory eop = callstack.convertStackMemOp(v) #print eop smt_conds.add(eq.getEq(v,eop)) new_mvars.add(eop) mvars = set(filter(lambda o: not (o.isMem()), mvars)) mvars = mvars.union(new_mvars) # we update the current call for next instruction callstack.prevInstruction(ins_str) #op.name = op.name+"_0" smt_conds.solve() smt_conds.write_smtlib_file("exp.smt2") smt_conds.write_sol_file("exp.sol") if (smt_conds.is_sat()): print "Solution:", for v in free_variables: if v.isReg(): if (v in ssa_map): print v,smt_conds.getValue(ssa_map[v]) elif v.isMem(): sname, offset = stack.read(v) v.mem_source = sname print v, smt_conds.getValue(v) else: print "Not exploitable"
def getTypedValueFromCode(inss, callstack, initial_values, memory, op, debug = False): # Initialization # we reverse the code order inss.reverse() # we reset the used memory variables Memvars.reset() # we save the current callstack last_index = callstack.index # TODO: create a better interface # we set the instruction counter counter = len(inss)-1 # ssa and smt objects ssa = SSA() smt_conds = SMT() val_type = None mvars = set() if (op.isImm()): return op elif (op.isMem()): for i in range(op.size): name = op.mem_source+"@"+str(op.mem_offset+i) mvars.add(Operand(name, "BYTE", op.mem_source, op.mem_offset+i)) #print name else: # we will start tracking op mvars.add(op) # we start without free variables fvars = set() ssa.getMap(mvars, set(), set()) for ins_str in inss: #print inss.current, "->", ins_str.strip("\n") #print ins_str.strip("\n") #for v in mvars: # print v, # #print "" # pins = parse_reil(ins_str) ins = Instruction(pins, memory.getAccess(counter), mem_regs = False) ins_write_vars = set(ins.getWriteVarOperands()) ins_read_vars = set(ins.getReadVarOperands()) if len(ins_write_vars.intersection(mvars)) > 0: ssa_map = ssa.getMap(ins_read_vars.difference(mvars), ins_write_vars, ins_read_vars.intersection(mvars)) cons = conds.get(pins.instruction, Condition) condition = cons(ins, ssa_map) mvars = mvars.difference(ins_write_vars) mvars = ins_read_vars.union(mvars) smt_conds.add(condition.getEq()) # simple typing new_val_type = detectType(mvars, ins, counter, callstack) # additional conditions mvars = addAditionalConditions(mvars, ins, ssa, callstack, smt_conds) val_type = max(val_type, new_val_type) # no more things to do # we update the counter counter = counter - 1 # we update the current call for next instruction callstack.prevInstruction(ins_str) if val_type == None: val_type = "imm" for v in mvars: if not (v in initial_values): print "#Warning__", str(v), "is free!" setInitialConditions(ssa, initial_values, smt_conds) smt_conds.solve(debug) if op.isReg(): op.name = op.name+"_0" elif op.isMem(): op.mem_source = op.mem_source+"_0" callstack.index = last_index # TODO: create a better interface if (debug): print val_type, op, smt_conds.getValue(op) return mkVal(val_type, smt_conds.getValue(op))
from AdditionSpecification import * from DivisionSpecification import * from SubtractionSpecification import * from sympy.ntheory import factorint from Operand import * print type(factorint(12).keys()) # Addition stuff operand1 = Operand() operand1.digits = {1.0: 8, 10.0: 1} operand2 = Operand() operand2.digits = {1.0: 3, 10.0: 8} operand3 = Operand() operand3.digits = {1.0: 1} operand4 = Operand() operand4.digits = {1.0: 3} problemSpecification = AdditionSpecification(operand1, operand2, [], lambda x, y: (x + y), {}, {}) print problemSpecification.digitAdjustments(operand1, operand2) print problemSpecification.digitAdjustments(operand1, operand3) print problemSpecification.digitAdjustments(operand1, operand4) # Subtraction stuff operand1 = Operand()