def getDependencies( self, gadgetDep ): """ getDependencies : Computes dependencies for this register, and store them into gadgetDep Return the dependencies as a list of couples ( Expression, Condition ) """ # If dependency already calculated if( gadgetDep.regDep.get(self.reg) != None ): return gadgetDep.regDep[self.reg] # Else we build it : # If ind is 0 then we reached a leaf of the graph because it's the initial value of the register if( self.reg.ind == 0 ): res = [[SSAExpr(self.reg), CTrue() ]] gadgetDep.regDep[self.reg] = res return res # Else we test if bad node # -> this is an un-understandable but harmless bug, additionnal empty nodes are added to the graph # don't know why they are created yet .... :/ if( len(self.outgoingArcs) == 0 and isinstance( self.expr, SSAExpr ) and self.reg == self.expr.reg): return [] # Here we know there are dependencies to compute, so we continue to explore the graph # We take into account the potential previous conditional jmps resCond = CurrentAnalysis.graph.condPath[self.jmpLvl] resExpr = self.expr # WARNING : we suppose that each node has only one or no arc towards MEM, not more ! resPrec = [[self.expr, resCond]] # For each arc, we replace step by step the expressions res = [] for a in self.outgoingArcs: res = [] # For each arc we get a list replacement value and the condition if( isinstance( a.dest, MEMNode )): dep = a.dest.getDependencies( a.num, a.label, a.size, gadgetDep ) else: dep = a.dest.getDependencies( gadgetDep ) for path in dep: if ( isinstance( a.dest, MEMNode )): res += [[p[0].replaceMemAcc(a.label, path[0]), Cond(CT.AND, p[1],path[1])] for p in resPrec ] elif( isinstance( a.dest, ITENode )): res += [[p[0].replaceITE(path[0]), Cond(CT.AND, p[1],path[1])] for p in resPrec ] else: res += [[p[0].replaceReg( a.dest.reg, path[0] ), Cond(CT.AND, p[1],path[1])] for p in resPrec ] resPrec = res if( len(res) == 0 ): res = [[self.expr, resCond]] # Taking into account the potential conditionnal jumps # We add the case when the jump had not been taken if( self.jmpLvl > 0 ): dep = CurrentAnalysis.graph.nodes[str(regBeforeLastJmp(self.reg, self.jmpLvl))].getDependencies( gadgetDep ) for d in dep: res.append( [d[0], Cond(CT.AND, d[1], CurrentAnalysis.graph.condJmps[self.jmpLvl])]) gadgetDep.regDep[self.reg] = res return res
def lookUpEXPRtoREG(self, expr, n=10): """ Return at most n gadgets that correspond to expr """ i = 0 found = 0 res = [] while (i < len(self.expr_list) and len(res) < n): cond = Cond(CT.EQUAL, self.expr_list[i], expr) if (cond.isTrue(hard=True)): res += self.gadget_list[i] i = i + 1 return res
def getDependencies(self, gadgetDep): resPrec = [[self.iftrue, self.cond]] # We first compute when the condition is TRUE : # For each arc, we replace step by step the expressions for a in self.outgoingArcs: resTrue = [] # For each arc we get a list replacement value and the condition if (isinstance(a.dest, MEMNode)): dep = a.dest.getDependencies(a.num, a.label, a.size, gadgetDep) else: dep = a.dest.getDependencies(gadgetDep) for path in dep: if (isinstance(a.dest, MEMNode)): resTrue += [[ p[0].replaceMemAcc(a.label, path[0]), Cond(CT.AND, p[1], path[1]) ] for p in resPrec] else: resTrue += [[ p[0].replaceReg(a.dest.reg, path[0]), Cond(CT.AND, p[1], path[1]) ] for p in resPrec] resPrec = resTrue res = resPrec resPrec = [[self.iffalse, self.cond.invert()]] # Then the case when the condition is FALSE # For each arc, we replace step by step the expressions for a in self.outgoingArcsCond: resFalse = [] # For each arc we get a list replacement value and the condition if (isinstance(a.dest, MEMNode)): dep = a.dest.getDependencies(a.num, a.label, a.size, gadgetDep) else: dep = a.dest.getDependencies(gadgetDep) for path in dep: if (isinstance(a.dest, MEMNode)): resFalse += [[ p[0].replaceMemAcc(a.label, path[0]), Cond(CT.AND, p[1], path[1]) ] for p in resPrec] else: resFalse += [[ p[0].replaceReg(a.dest.reg, path[0]), Cond(CT.AND, p[1], path[1]) ] for p in resPrec] resPrec = resFalse res = res + resPrec return res
def lookUpCSTtoMEM(self, addr, cst, n=10): """ Returns gadgets numbers that put cst at mem(addr) as a list of gadgets uids cst - (int) addr - Expr """ i = 0 found = 0 res = [] # Iterate for all write addresses while (i < len(self.addr_list) and len(res) < n): # If we have a dependencie for the requested constant if (cst in self.written_values[i]): # Comparing the addresses with hard=True so we call z3 solver cond = Cond(CT.EQUAL, self.addr_list[i], addr) if (cond.isTrue(hard=True)): res += self.written_values[i][cst] i = i + 1 return res
def flattenITE(self): newArgs = [a[0].flattenITE() for a in self.args] listArgs = [[[], CTrue()]] tmp = [] for listA in newArgs: for a in listA: for arg in listArgs: tmp.append(arg[0] + [a[0]], Cond(CT.AND, arg[1], a[1])) listArgs = tmp # listArgs contains now an array of couples ( list, condition ) res = [[Cat(a[0]), a[1]] for a in listArgs] return res
def flattenITE(self): """ !!! Works only for unary or binary operations """ res = [] if (len(self.args) == 2): flatLeft = self.args[0].flattenITE() flatRight = self.args[1].flattenITE() for f in flatLeft: for f2 in flatRight: res.append([ Op(self.op, [f[0], f2[0]]), Cond(CT.AND, f[1], f2[1]) ]) return res elif (len(self.args) == 1): flat = self.args[0].flattenITE() return [[Op(self.op, self.size, f[0]), f[1]] for f in flat] else: raise ExprException( " flattenITE can not be used with an operator on more than 2 arguments (%d here) " % len(self.args))
def getMemDependencies( self, gadgetDep ): """ Extract dependencies from memory Dependencies of the memory are returned in the form of a dictionnary of lists of couples - Dictionnary entries are addresses where the memory is accessed ( like R7_0 + R6_0 + 0x56 ) - Dictionnary values are lists that are classical dependencies. This means lists of couples [value, condition] Parameters: (gadgetDep) (GadgetDependencies) The structure where to store the memory dependencies """ #raise GraphException("Function getMemDependencies() not yet implemented, sorry !") # The three dictionnaries below MUST have the exact same keys res = dict() # Key: Expr (address of store), Value: list of dependencies previousStoreSizes = dict() # Key: Expr (address of store), Value: size of the store tmpCond = None # We go through memory-writes in chronological order for a in sorted(self.outgoingArcs): # Key to manipulate the current store address addrKey = str(simplify(a.label.toZ3())) # We get the dependency for the node that is written in memory dep = a.dest.getDependencies(gadgetDep) # !!! HERE WE DON'T NEED TO CORRECT THE DEPENDENCIES WITH THE self.storedValues BECAUSE IT HAS ALREADY BEEN DONE FOR REGISTERS AND WE USE THEIR FINAL DEPENDENCIES DIRECTLY if( isinstance(a.dest, SSANode)): dep = [[self.storedValues[a.num].replaceReg(a.dest.reg, d[0]), d[1]] for d in dep ] # Handling conditionnal jumps if( tmpCond is None ): # If first memory access, then we get the condPath for the adequate level (condition that must be true so that we haven't jumper out from the gadget) tmpCond = CurrentAnalysis.graph.condPath[a.jmpLvl] else: # Else we only add the condJump corresponding to this level tmpCond = Cond( CT.AND, tmpCond, CurrentAnalysis.graph.condJmps[a.jmpLvl].invert() ) storeLen = a.size / 8 # Number of bytes written by this arc previousStoreSizes[a.label] = storeLen #readDict = {addrKey:0} # Basic constraint for optimisation # Create a new dependency for this # Updating dependencies for the previous memory-store resTmp = dict() for writeAddr, prevDep in res.iteritems(): # 1st Case Preparation : New store doesn't affect old store # Get the size of the previous by looking at one dependency (little hack and not so clean but heh) previousStoreSize = previousStoreSizes[writeAddr] higher = Op( "Add", [writeAddr, ConstExpr(previousStoreSize, writeAddr.size)] ) lower = Op( "Sub", [writeAddr, ConstExpr( a.size, writeAddr.size )]) outCond = Cond( CT.OR, Cond( CT.GE, a.label, higher ), Cond( CT.LE, a.label, lower )) newDict = {addrKey:[1-storeLen, previousStoreSize-1]} newDep = [] # Update each old dependency for prev in prevDep: # 1st Case Still... if( self.compatibleAccesses( prev[2], newDict )): newDep.append( [prev[0], Cond( CT.AND, outCond, prev[1]), self.dictFusion(newDict, prev[2])]) # Now 2d Case: New store overwrites old store # ... # Offset = current store - previous store for offset in range(1-storeLen, previousStoreSize-1): newDict = {addrKey:offset} newStorePossibleValue = Op("Add", [writeAddr, ConstExpr(offset, writeAddr.size)] ) addrCond = Cond( CT.EQUAL, a.label, newStorePossibleValue ) if( self.compatibleAccesses( newDict, prev[2] ) and self.filterOffset( offset, previousStoreSize, storeLen )): # For each dep of the new store, we update with the case where it overwrites the previous stores for d in dep: newValue = self.overWrite( offset, d[0], prev[0] ) newCond = Cond( CT.AND, Cond(CT.AND, prev[1], addrCond ), d[1] ) newDep.append( [newValue, newCond, self.dictFusion(newDict, prev[2])]) resTmp[writeAddr] = newDep # New dependency: resTmp[a.label] = [] newStoreDict={addrKey:0} for d in dep: resTmp[a.label].append([d[0], d[1], newStoreDict]) # Save everything res = resTmp # ... # Clean the deps from dictionnaries for addr, dep in res.iteritems(): dep = [[].append([d[0], d[1]]) for d in dep ] # Replace the memory addresses of the stores by their basic dependencies # So far we have MEM[expr] where expr doesn't necessarily have basic dependencies, so we want to express everything with the basic dependencies gadgetDep.memDep = res
def getDependencies( self, num, addr, size, gadgetDep ): """ Extract dependencies from memory Parameters: (num) (int) The number of the arc in the graph that represents the load operation (addr) (Expr) The address at which memory is read (size) (int) The size in bits of the read (gadgetDep) (GadgetDependencies) The structure used to compute dependencies """ res = [] tmp = num readLen = size / 8 addrID = -1 # Used to optimise combinations without calling the solver... addrKey = str(simplify(addr.toZ3())) readDict = {addrKey:0} # Basic constraint for optimisation value = MEMExpr( addr, size ) resTmp = [[MEMExpr(addr, size), CTrue(), readDict]] tmpCond = None # For each outgoing arc, i.e for each memory write for a in sorted( self.outgoingArcs, reverse=False): optWriteKey = str(simplify(a.label.toZ3())) # key for the store address for optimisation # If a.num > num, then we have checked all memory writes before the read we compute dependencies for if( a.num >= num ): break # Get the dependencies for the register written in memory dep = a.dest.getDependencies( gadgetDep ) # We correct the dependencies in case we stored an expression and not only a register or constant if( isinstance(a.dest, SSANode)): dep = [[self.storedValues[a.num].replaceReg(a.dest.reg, d[0]), d[1]] for d in dep ] # Handle previous conditional jumps if( tmpCond is None ): # If first memory access, then we get the condPath for the adequate level (condition that must be true so that we haven't jumper out from the gadget) tmpCond = CurrentAnalysis.graph.condPath[a.jmpLvl] else: # Else we only add the condJump corresponding to this level tmpCond = Cond( CT.AND, tmpCond, CurrentAnalysis.graph.condJmps[a.jmpLvl].invert() ) storeLen = a.size / 8 # Number of bytes written by this arc # For each dependency, we update the resTmp into some new array of dependencies # This models a store operation overwriting previous memory contents newResTmp = [] # For each case, update possibilities for prev in resTmp: # For each dependency of the register written in memory for d in dep: # We consider every possibility of memory overwrite ( offset between the read and write ). Some could modify 1 byte, others 2 bytes, etc... for offset in range( 1-storeLen, readLen-1 ): writeAddr = Op( "Add", [addr, ConstExpr( offset, addr.size )]) newDict = {optWriteKey:offset} if( self.compatibleAccesses( prev[2], newDict ) and self.filterOffset( offset, storeLen, readLen )): newValue = self.overWrite( offset, d[0], prev[0] ) addrCond = Cond( CT.EQUAL, a.label, writeAddr ) newCond = Cond( CT.AND, Cond(CT.AND, prev[1], addrCond ), d[1] ) newResTmp.append( [newValue, newCond, self.dictFusion(newDict, prev[2])]) # We keep the previous ones if the store is made out of the bounds of the read higher = Op( "Add", [addr, ConstExpr(readLen, addr.size)] ) lower = Op( "Sub", [addr, ConstExpr( storeLen, addr.size )]) outCond = Cond( CT.OR, Cond( CT.GE, a.label, higher ), Cond( CT.LE, a.label, lower )) newDict = {optWriteKey:[1-storeLen, readLen-1]} for prev in resTmp: if( self.compatibleAccesses( prev[2], newDict )): newResTmp.append( [prev[0], Cond( CT.AND, outCond, prev[1]), self.dictFusion(newDict, prev[2])] ) # give resTmp its new value resTmp = newResTmp # Extract only the values and conditions res = [[d[0], d[1]] for d in resTmp] return res
def simplifyDependencies( self ): """ Simplifies basic operations, conditions... in order to have only basic dependencies A 'basic' dependency is an expression in which SSA Registers have only null indexes ( R?_0 ) """ # (1) First compute basic dependencies for registers # replaceTable stores the registers that could be replaced by a basic dependency replaceTable = {} # For each register get the best basic dep we have for reg in CurrentAnalysis.graph.getRegisters(): # if only one dependency, i.e no other possibility of end-value if( self.regDep.get(reg) != None and len(self.regDep[reg]) == 1 ): replaceTable[reg] = self.regDep[reg][0][0] self.regDep[reg][0][1] = CTrue() # Repeated procedure until no register with index > 0 is found stop = False while( not stop): stop = True # I know... the code below is very nasty # ---> should at least rename variables # For each register 'reg' we simplifify its dependencies for reg in self.regDep.keys(): # At the end of the loop, 'newDeps' shall be new dependencies of register 'reg' newDeps = [] # For each dependency 'dep' of 'reg' for dep in self.regDep[reg]: # At the end of the loop, 'newDepDeps' shall be the dependency 'dep' simplified in one or many dependencies newDepDeps = [[dep[0], dep[1]]] # Here newDepdeps <=> [dep] # We start by simplifying the value # For each register in the expression for register in dep[0].getRegisters(): if( register.ind > 0 ): stop = False # We replace it by its own dependencies # If 'register' has only one dependency -> direct replacement if( replaceTable.get(register) != None ): newDepDeps[0][0] = dep[0].replaceReg( register, replaceTable[register] ) # Else we go through them and make the replacement else: tmpDep = [] for depdep in self.regDep[register]: for newDepDep in newDepDeps: depVal = newDepDep[0].replaceReg( register, depdep[0] ) depCond = Cond( CT.AND, newDepDep[1], depdep[1]) tmpDep.append( [depVal, depCond]) newDepDeps = tmpDep # Then same thing for the condition # !! The only condition of the dependency 'dep' can now have been transformed into several ones depdending on the previous simplification of the value, into list newDepDeps # So we iterate into 'dep' transformed into a list of dependencies 'gDep' ( Generated DEPendencies ) newResTruc = [] for gDep in newDepDeps: newDepDepsF = [[gDep[0], gDep[1]]] # Same operation than before for register in gDep[1].getRegisters(): if( register.ind > 0 ): stop = False if( replaceTable.get(register) != None ): newDepDepsF[0][1] = gDep[1].replaceReg( register, replaceTable[register]) else: tmpDep = [] for depdep in self.regDep[register]: for newDepDep in newDepDepsF: depVal = newDepDep[0] depCond = Cond( CT.AND, newDepDep[1].replaceReg(register,depdep[0]) , depdep[1]) tmpDep.append( [depVal, depCond] ) newDepDepsF = tmpDep newResTruc += newDepDepsF newDepDepsF = newResTruc # Adding the simplified 'dep' in the dependency list of 'reg' newDeps += newDepDepsF # The new dependencies of 'reg' are 'newDeps' ;-) self.regDep[reg] = newDeps # (2) Then basic dependencies for the memory for addr, deps in self.memDep.iteritems(): newMemDep = dict() newDepsForAddr = [] # Compute the basic dependency for the dependency list # First for registers for val, cond in deps: # Replace all registers in the value field # To get a new list of (val, cond) newDepsForVal = [[val, cond]] # For each register, we replace in all dependencies for reg in val.getRegisters(): # Replace by all the possible values for this register for regdep in self.regDep[reg]: tmpNewDepsForVal = [] # Update the temporary results for tmpDep in newDepsForVal: tmpNewDepsForVal.append([tmpDep[0].replaceReg( reg, regdep[0] ), Cond(CT.AND, regdep[1], tmpDep[1])]) newDepsForVal = tmpNewDepsForVal # Replace all registers in condition fields newDepsForCond = newDepsForVal for reg in cond.getRegisters(): for regdep in self.regDep[reg]: tmpNewDepsForCond = [] # Update the temporary results for tmpDep in newDepsForCond: tmpNewDepsForCond.append( [tmpDep[0], Cond(CT.AND, regdep[1], tmpDep[1].replaceReg(reg, regdep[0]))]) newDepsForCond = tmpNewDepsForCond # Update the list of dependencies (now basic) for addr newDepsForAddr += newDepsForCond # Compute the basic dependencies for the memory address addr addrDeps = [[addr, CTrue()]] for reg in addr.getRegisters(): for regdep in self.regDep[reg]: tmpAddrDeps = [] for tmpDep in addrDeps: tmpAddrDeps.append([tmpDep[0].replaceReg(reg, regdep[0]), Cond(CT.AND, tmpDep[1], regdep[1]) ]) addrDeps = tmpAddrDeps # TODO : SIMPLIFY EXPRESSIONS HERE, OR IN THE BEGGINIG OF THE FUNCTION ??? # Now combine the addrDeps and the newDepsForAddr in the final result for the gadget for newAddrDep in addrDeps: updateNewAddrDeps = [] for dep in newDepsForAddr: updateNewAddrDeps.append([dep[0], Cond(CT.AND, dep[1], newAddrDep[1])]) newAddr = newAddrDep[0].simplify() newMemDep[newAddr] = updateNewAddrDeps self.memDep = newMemDep # Remove the dependencies for intermediate registers ( we don't care about R1_1 anymore if we have R1_3 in the end :/ ) for reg in self.regDep.keys(): if( reg.ind < CurrentAnalysis.gadget.regCount[reg.num]): del self.regDep[reg] # Cleaning and simplifying for reg in self.regDep.keys(): for dep in self.regDep[reg]: dep[0] = dep[0].simplify() dep[1].clean() for addr, deps in self.memDep.iteritems(): for dep in deps: dep[0] = dep[0].simplify() dep[1].clean()
def buildGraph(self, irsb): # (1) Initialisations... self.valuesTable = {} # Keys are whatever, Values are Expr self.graph = Graph() # Empty graph CurrentAnalysis.gadget = self CurrentAnalysis.graph = self.graph self.regCount = {} # Update the index counts and nodes for registers that already have a translation into generic IR for reg in Analysis.regNamesTable.values(): self.regCount[reg] = 0 node = SSANode(SSAReg(reg, 0), SSAExpr(SSAReg(reg, 0))) self.graph.nodes["R%d_0" % reg] = node self.graph.nodes["MEM"] = MEMNode() memAccCount = 0 # Hold the number of the next arc incoming/outgoing to/from the memory node # (2) Graph construction... # Iterate through all reill instructions : for i in range(0, len(irsb)): instr = irsb[i] self.nbInstr = self.nbInstr + 1 # Translating different types of instructions if (instr.mnemonic == ReilMnemonic.NOP): pass # Basic operations ( ADD, SUB, MUL, etc ) elif (isCalculationInstr(instr.mnemonic)): if (instr.operands[2]._name[0] == "t"): expr = self.barfCalculationToExpr(instr.mnemonic, instr.operands[0], instr.operands[1], instr.operands[2].size) self.valuesTable[instr.operands[2]._name] = expr else: expr = self.barfCalculationToExpr(instr.mnemonic, instr.operands[0], instr.operands[1], instr.operands[2].size) regExpr = self._getReg(instr.operands[2]._name) reg = regExpr.reg expr = self.translateFullRegAssignement( expr, instr.operands[2]) reg = SSAReg(reg.num, reg.ind + 1) self.valuesTable[str(reg)] = expr # Adding the node node = SSANode(reg, expr) # Adding arcs toward other nodes and memory for dep in expr.getRegisters(): # Dep is a SSAReg on which node depends node.outgoingArcs.append( Arc(self.graph.nodes[str(dep)])) for mem in expr.getMemAcc(): addr = mem[0] size = mem[1] node.outgoingArcs.append( Arc(self.graph.nodes["MEM"], memAccCount, addr, size)) memAccCount += 1 self.graph.nodes[str(reg)] = node self.regCount[reg.num] += 1 # Memory load instruction elif (isLoadInstr(instr.mnemonic)): expr = self.barfLoadToExpr(instr.operands[0], instr.operands[2]) if (instr.operands[2]._name[0] == "t"): self.valuesTable[instr.operands[2]._name] = expr else: regExpr = self._getReg(instr.operands[2]._name) reg = regExpr.reg expr = self.translateFullRegAssignement( expr, instr.operands[2]) reg.ind += 1 self.regCount[reg.num] += 1 self.valuesTable[str(reg)] = expr # Adding node towards memory node = SSANode(reg, expr) for mem in expr.getMemAcc(): addr = mem[0] size = mem[1] node.outgoingArcs.append( Arc(self.graph.nodes["MEM"], memAccCount, addr, size)) memAccCount += 1 self.graph.nodes[str(reg)] = node # Memory store instruction elif (isStoreInstr(instr.mnemonic)): expr = self.barfOperandToExpr(instr.operands[0]) addr = self.barfOperandToExpr(instr.operands[2]) #if( isinstance( instr.operands[0], ReilImmediateOperand )): #node = ConstNode( instr.operands[0].immediate, instr.operands[0].size ) #self.graph.nodes["MEM"].outgoingArcs.append( Arc( node, memAccCount, addr, expr.size )) if (isinstance(expr, ConstExpr)): node = ConstNode(expr.value, expr.size) self.graph.nodes["MEM"].outgoingArcs.append( Arc(node, memAccCount, addr, expr.size)) elif (not expr.getRegisters()): raise GadgetException( "Expression is neither ConstExpr nor has registers and should be written in memory ? - not yet supported!" ) else: self.graph.nodes["MEM"].outgoingArcs.append( Arc(self.graph.nodes[str(expr.getRegisters()[0])], memAccCount, addr, expr.size)) self.graph.nodes["MEM"].storedValues[memAccCount] = expr memAccCount += 1 # Transfer value into register elif (isPutInstr(instr.mnemonic)): if (instr.operands[2]._name[0] == "t"): expr = self.barfOperandToExpr(instr.operands[0]) if (instr.operands[2].size != expr.size): expr = Convert(instr.operands[2].size, expr) self.valuesTable[instr.operands[2]._name] = expr else: regExpr = self._getReg(instr.operands[2]._name) reg = regExpr.reg expr = self.barfOperandToExpr(instr.operands[0]) if (instr.operands[0].size < instr.operands[2].size): expr = self.translateFullRegAssignement( expr, instr.operands[2]) else: expr = Convert(instr.operands[2].size, expr) if (instr.operands[2].size != REGSIZE.size): expr = self.translateFullRegAssignement( expr, instr.operands[2]) regDep = expr.getRegisters() memDep = expr.getMemAcc() # Adding node and arcs to the graph reg = SSAReg(reg.num, reg.ind + 1) node = SSANode(reg, expr) for r in regDep: node.outgoingArcs.append(Arc(self.graph.nodes[str(r)])) for mem in memDep: addr = mem[0] size = mem[1] node.outgoingArcs.append( Arc(self.graph.nodes["MEM"], memAccCount, addr, size)) memAccCount += 1 self.graph.nodes[str(reg)] = node self.regCount[reg.num] += 1 self.valuesTable[str(reg)] = expr # Boolean IS Zero instrution # BISZ( a, b ) has the following effects : # b <- 1 if a == 0 # b <- 0 if a != 0 elif (instr.mnemonic == ReilMnemonic.BISZ): zero = ConstExpr(0, instr.operands[0].size) ifzero = ConstExpr(1, instr.operands[2].size) ifnotzero = ConstExpr(0, instr.operands[2].size) testedValue = self.barfOperandToExpr(instr.operands[0]) # If operands[0] == 0 then the value assigned to operands[2] is 1. # If operands[0] != 0 then operands[2] becomes 0 cond = Cond(CT.EQUAL, testedValue, zero) expr = ITE(cond, ifzero, ifnotzero) if (instr.operands[2]._name[0] == "t"): self.valuesTable[instr.operands[2]._name] = expr else: regExpr = self._getReg(instr.operands[2]._name) reg = regExpr.reg reg.ind += 1 self.regCount[reg.num] += 1 self.valuesTable[str(reg)] = expr # Adding node to the graph # Creation of the ite node iteNode = ITENode(cond, ifzero, ifnotzero) # Link the real node to the ITE node node = SSANode(reg, Convert(REGSIZE.size, expr)) node.outgoingArcs.append(Arc(iteNode)) self.graph.nodes[str(reg)] = node # Conditionnal jump elif (instr.mnemonic == ReilMnemonic.JCC): # Determine the address where to jmp # If jmp to a fixed location if (isinstance(instr.operands[2], ReilImmediateOperand) and instr.operands[2].size != 40 and instr.operands[2].size != 72): addr = ConstExpr(instr.operands[2].immediate) addr.size = REGSIZE.size # If it is a pointer elif (instr.operands[2].size == 40 or instr.operands[2].size == 72): #We test if the value is less than 0x100 # If yes, then we ignore the condition because unable to compute dependencies # This kind of small values depict an instruction simulated in many basic blocks # We do not handle conditions for the Instr Pointer INSIDE a gadget if (isinstance(instr.operands[2], ReilImmediateOperand) and (instr.operands[2].immediate >> 8) - self.addr < 0x1): raise GadgetException( "REIL instruction(s) too complicated to emulate in gadget:\n" + self.asmStr) # We also test if there is a jump inside the bounds of the gadget # For now those are also not supported # Sadly, this also suppresses some conditional conditions if (isinstance(instr.operands[2], ReilImmediateOperand) and (instr.operands[2].immediate >> 8) - self.addr < (len(self.hexStr) / 4)): raise GadgetException( "Jumps inside the gadget are not handled yet in gadget:\n" + self.asmStr) # Get the real return/jump address with an 8 bit SHR expr = self.barfOperandToExpr(instr.operands[2]) addr = Extr(expr.size - 1, 8, expr) else: addr = self.barfOperandToExpr(instr.operands[2]) ip = self._getReg( Analysis.ArchInfo.ip).reg # Get the instruction pointer ip.ind += 1 # Quick check if simple 'ret' so the expression is easier to read # A jump is always taken if first argument is a constant != 0 if (isinstance(instr.operands[0], ReilImmediateOperand) and instr.operands[0].immediate != 0): self.valuesTable[str(ip)] = addr expr = addr node = SSANode(ip, Convert(REGSIZE.size, addr)) for dep in addr.getRegisters(ignoreMemAcc=True): node.outgoingArcs.append( Arc(self.graph.nodes[str(dep)])) for mem in addr.getMemAcc(): address = mem[0] size = mem[1] node.outgoingArcs.append( Arc(self.graph.nodes["MEM"], memAccCount, address, size)) memAccCount += 1 self.regCount[ip.num] += 1 self.graph.nodes[str(ip)] = node # Else processing conditional jump else: # Create node and stuff reg = ip zero = ConstExpr(0, instr.operands[0].size) cond = Cond(CT.NOTEQUAL, self.barfOperandToExpr(instr.operands[0]), zero) # Update the info about conditional jumps in Graph.py module self.graph.condJmps.append(cond) self.graph.condPath.append( Cond(CT.AND, cond.invert(), self.graph.condPath[self.graph.countCondJmps])) # TODO : this seems not to put the good address into nextInstrAddr ??? nextInstrAddr = ConstExpr(irsb[i + 1].address >> 8, REGSIZE.size) # 1 - FIRST STEP : ITE Node to say : IP takes new value OR stays the same expr = ITE(cond, addr, ConstExpr(instr.address >> 8, REGSIZE.size)) self.valuesTable[str(reg)] = expr # Adding node to the graph # Creation of the ite node # We consider that either the jump is taken ( to addr ) # or the value stays the one of the current instruction ( instr.address >> 8 ) iteNode = ITENode( cond, addr, ConstExpr(instr.address >> 8, REGSIZE.size)) for dep in addr.getRegisters(): iteNode.outgoingArcs.append( Arc(self.graph.nodes[str(dep)])) for mem in addr.getMemAcc(): address = mem[0] size = mem[1] iteNode.outgoingArcs.append( Arc(self.graph.nodes["MEM"], memAccCount, address, size)) memAccCount += 1 #iteNode.outgoingArcsCond.append( Arc( self.graph.nodes[str(prevOcc(reg))], -1, None)) # Link the real node to the ITE node node = SSANode(reg, expr) node.outgoingArcs.append(Arc(iteNode)) self.graph.nodes[str(reg)] = node # And do not forget self.regCount[reg.num] += 1 self.graph.countCondJmps += 1 # 2 - SECOND STEP # We update the register again with the address of the next instruction reg = SSAReg(reg.num, reg.ind + 1) node = SSANode(reg, nextInstrAddr) self.graph.nodes[str(reg)] = node self.regCount[reg.num] += 1 else: raise GadgetException( "REIL operation <%s> not supported in gadget:\n%s" % (ReilMnemonic.to_string(instr.mnemonic), self.asmStr))