def regBeforeLastJmp(reg, jmpLvl): """ Returns the SSA occurence of the register 'reg' that corresponds to it's value just before the jump number 'jmpLvl' was taken Parameters: (reg) (SSAReg) The regiter we are considering (jmpLvl) (int) The jump level we want to go before """ ind = reg.ind num = reg.num while( ind != 0 and CurrentAnalysis.graph.nodes[str(SSAReg(num, ind))].jmpLvl >= jmpLvl ): ind -= 1 return SSAReg(num ,ind)
def hasPossibleNormalRet(self): """ Checks if the gadget can return normaly under certain conditions ! Returns a pair (True, condition) (False, None) """ if (self.sort != GadgetSort.REGULAR): False # Check ip = SSAReg( Analysis.regNamesTable[Analysis.ArchInfo.ip], self.graph.lastMod[Analysis.regNamesTable[Analysis.ArchInfo.ip]]) sp_num = Analysis.n2r(Analysis.ArchInfo.sp) for dep in self.dep.regDep.get(ip, []): dep[0] = dep[0].simplify() if (isinstance(dep[0], MEMExpr)): addr = dep[0].addr (isInc, inc) = addr.isRegIncrement(sp_num) # Normal ret if the final value of the IP is value that was in memory before the last modification of SP ( i.e final_IP = MEM[final_sp - size_of_a_register ) if (isInc and self.spInc and inc == (self.spInc - (Analysis.ArchInfo.bits / 8))): return (True, dep[1]) return (False, None)
def calculateSpInc(self): """ Compute how much the stack pointer has advanced after this gadget is executed """ if (self.sort != GadgetSort.REGULAR): return if (self.duplicate): self.spInc = self.duplicate.spInc return sp_num = Analysis.regNamesTable[Analysis.ArchInfo.sp] if (not sp_num in self.graph.lastMod): self.spInc = 0 return sp = SSAReg(sp_num, self.graph.lastMod[sp_num]) if (not sp in self.dep.regDep): self.spInc = 0 return for dep in self.dep.regDep[sp]: if (dep[1].isTrue()): (isInc, inc) = dep[0].isRegIncrement(sp.num) if (isInc): self.spInc = inc return else: self.spInc = None return
def calculateRet(self): """ Computes the return address, checks if it is valid or not... /!\ MUST be called after calculateSpInc() """ ip = SSAReg( Analysis.regNamesTable[Analysis.ArchInfo.ip], self.graph.lastMod[Analysis.regNamesTable[Analysis.ArchInfo.ip]]) sp_num = Analysis.regNamesTable[Analysis.ArchInfo.sp] if (not self.spInc): self.normalRet = False return if (not ip in self.dep.regDep): self.normalRet = False return for dep in self.dep.regDep[ip]: if (dep[1].isTrue()): if (isinstance(dep[0], MEMExpr)): addr = dep[0].addr (isInc, inc) = addr.isRegIncrement(sp_num) # Normal ret if the final value of the IP is value that was in memory before the last modification of SP ( i.e final_IP = MEM[final_sp - size_of_a_register ) if (isInc and inc == (self.spInc - (Analysis.ArchInfo.bits / 8))): self.normalRet = True else: self.normalRet = False
def calculateRet(self): """ Computes the return address, checks if it is valid or not... /!\ MUST be called after calculateSpInc() """ if (self.sort != GadgetSort.REGULAR): return if (self.duplicate): self.ret = self.duplicate.ret self.retValue = self.duplicate.retValue return ip = SSAReg( Analysis.regNamesTable[Analysis.ArchInfo.ip], self.graph.lastMod[Analysis.regNamesTable[Analysis.ArchInfo.ip]]) sp_num = Analysis.regNamesTable[Analysis.ArchInfo.sp] if (self.spInc == None): self.ret = RetType.UNKNOWN return if (not ip in self.dep.regDep): self.ret = RetType.UNKNOWN return for dep in self.dep.regDep[ip]: if (dep[1].isTrue()): if (isinstance(dep[0], MEMExpr)): addr = dep[0].addr (isInc, inc) = addr.isRegIncrement(sp_num) # Normal ret if the final value of the IP is value that was in memory before the last modification of SP ( i.e final_IP = MEM[final_sp - size_of_a_register ) if (isInc and inc == (self.spInc - (Analysis.ArchInfo.bits / 8))): self.ret = RetType.RET else: self.ret = RetType.UNKNOWN elif (isinstance(dep[0], SSAExpr)): # Try to detect gadgets ending by 'call' if (self.ins[-1]._mnemonic[:4] == "call"): self.ret = RetType.CALL_REG self.retValue = dep[0].reg.num else: self.retValue = dep[0].reg.num self.ret = RetType.JMP_REG return self.ret = RetType.UNKNOWN
def _getReg(self, regStr): """ Given a register name ( eax, edx, ... ) this function translates it into a generic R0_ R1_ ... This is meant to return the current state of a register under the form of a SSA register, by checking into the regCount dictionnary. If eax ( whose id is 2 for example ) have been modified 2 times, then the current value of eax will be _getReg("eax") = SSAReg(2,2) If the register had never occured before, either in the gadget, or during the whole analysis, then it's ID is created and added to the regNamesTable. If the argument does not depict a full register the translation of the full register is returned ---> e.g _getReg("AH") -> _getReg("RAX") -> R3 Parameters : regStr - (str) The string of the register we want to get """ # We first find the corresponding full register ( like AH ---> RAX ) aliasMapper = Analysis.ArchInfo.currentArchInfo.alias_mapper if (aliasMapper.get(regStr) != None): if (aliasMapper[regStr][0] != "rflags" and aliasMapper[regStr][0] != "eflags"): fullReg = aliasMapper[regStr] # Couple (reg, offset) regStr = fullReg[0] # If first occurence of this register in analysis : we translate it in the table if (Analysis.regNamesTable.get(regStr) == None): Analysis.regNamesTable[regStr] = Analysis.ssaRegCount Analysis.revertRegNamesTable[Analysis.ssaRegCount] = regStr self.regCount[Analysis.ssaRegCount] = 0 reg = SSAReg(Analysis.ssaRegCount, 0) regExpr = SSAExpr(reg) Analysis.ssaRegCount += 1 # Create basic node node = SSANode(reg, regExpr) node.jmpLvl = 0 self.graph.nodes[str(reg)] = node # Else if register already have an ID else: reg = SSAReg(Analysis.regNamesTable[regStr], None) # First occurence in this gadget -> ind set to 0 and node created if (self.regCount.get(reg.num) == None): reg.ind = 0 self.regCount[reg.num] = 0 # Create basic node node = SSANode(reg, SSAExpr(reg)) node.jmpLvl = 0 self.graph.nodes[str(reg)] = node else: reg.ind = self.regCount[reg.num] if (self.graph.nodes.get(str(reg)) == None): self.graph.nodes[str(reg)] = SSANode(reg, SSAExpr(reg)) # Returning corresponding expression return SSAExpr(reg)
def __init__(self, reg, expr): Node.__init__(self) self.reg = SSAReg(reg.num, reg.ind) self.expr = expr
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))