def build_mprotect64(addr, size, prot=7, constraint=None, assertion=None, clmax=SYSCALL_LMAX, optimizeLen=False): """ Call mprotect from X86-64 arch Args must be on registers (rdi, rsi, rdx): Sizes are (unsigned long, size_t, unsigned long) rax must be 10 """ # Check args if not isinstance(addr, int): error("Argument error. Expected integer, got " + str(type(addr))) return None elif not isinstance(size, int): error("Argument error. Expected integer, got " + str(type(size))) return None elif not isinstance(prot, int): error("Argument error. Expected integer, got " + str(type(prot))) return None if( constraint is None ): constraint = Constraint() if( assertion is None ): assertion = Assertion() # Check if we have the function ! verbose("Trying to call mprotect() function directly") func_call = build_call('mprotect', [addr, size, prot], constraint, assertion, clmax=clmax, optimizeLen=optimizeLen) if( not isinstance(func_call, str) ): verbose("Success") return func_call else: if( not constraint.chainable.ret ): verbose("Coudn't call mprotect(), try direct syscall") else: verbose("Couldn't call mprotect() and return to ROPChain") return None # Otherwise do the syscall by 'hand' # Set the registers args = [[Arch.n2r('rdi'),addr],[Arch.n2r('rsi'), size],[Arch.n2r('rdx'),prot], [Arch.n2r('rax'),10]] chain = popMultiple(args, constraint, assertion, clmax-1, optimizeLen) if( not chain ): verbose("Failed to set registers for the mprotect syscall") return None # Syscall syscalls = search(QueryType.SYSCALL, None, None, constraint, assertion) if( not syscalls ): verbose("Failed to find a syscall gadget") return None else: chain.addChain(syscalls[0]) verbose("Success") return chain
def build_call_linux64(funcName, funcArgs, constraint, assertion, clmax=None, optimizeLen=False): # Arguments registers # (Args should go in these registers for x64) argsRegsNames = ['rdi','rsi','rdx','rcx', 'r8', 'r9'] argsRegs = [Arch.n2r(name) for name in argsRegsNames] # Find the address of the fonction (funcName2, funcAddr) = getFunctionAddress(funcName) if( funcName2 is None ): return "Couldn't find function '{}' in the binary".format(funcName) # Check if bad bytes in function address if( not constraint.badBytes.verifyAddress(funcAddr) ): return "'{}' address ({}) contains bad bytes".format(funcName2, string_special('0x'+format(funcAddr, '0'+str(Arch.octets()*2)+'x'))) # Check how many arguments if( len(funcArgs) > 6 ): return "Doesn't support function call with more than 6 arguments with Linux X64 calling convention :(" # Find a gadget for the fake return address if( funcArgs ): # Build the ropchain with the arguments args_chain = popMultiple(map(lambda x,y:(x,)+y, argsRegs[:len(funcArgs)], funcArgs), constraint, assertion, clmax=clmax, optimizeLen=optimizeLen) if( not args_chain): return "Couldn't load arguments in registers" else: # No arguments args_chain = ROPChain() # Build call chain (function address + fake return address) return args_chain.addPadding(funcAddr, comment=string_ropg(funcName2))
def build_mprotect64(addr, size, prot=7, constraint=None, assertion=None, clmax=None, optimizeLen=False): """ Call mprotect from X86-64 arch Args must be on registers (rdi, rsi, rdx): Sizes are (unsigned long, size_t, unsigned long) rax must be 10 """ # Check args if not isinstance(addr, int): error("Argument error. Expected integer, got " + str(type(addr))) return None elif not isinstance(size, int): error("Argument error. Expected integer, got " + str(type(size))) return None elif not isinstance(prot, int): error("Argument error. Expected integer, got " + str(type(prot))) return None if (constraint is None): constraint = Constraint() if (assertion is None): assertion = Assertion() # Set the registers args = [[Arch.n2r('rdi'), addr], [Arch.n2r('rsi'), size], [Arch.n2r('rdx'), prot], [Arch.n2r('rax'), 10]] chain = popMultiple(args, constraint, assertion, clmax - 1, optimizeLen) if (not chain): verbose("Failed to set registers for the mprotect syscall") return None # Syscall syscalls = search(QueryType.SYSCALL, None, None, constraint, assertion) if (not syscalls): verbose("Failed to find a syscall gadget") return None else: chain.addChain(syscalls[0]) verbose("Success") return chain
def build_mprotect32(addr, size, prot=7, constraint=None, assertion = None, clmax=None, optimizeLen=False): """ Call mprotect from X86 arch Args must be on the stack: int mprotect(void *addr, size_t len, int prot) args must be in registers (ebx, ecx, edx) eax must be 0x7d = 125 """ # Check args if not isinstance(addr, int): error("Argument error. Expected integer, got " + str(type(addr))) return None elif not isinstance(size, int): error("Argument error. Expected integer, got " + str(type(size))) return None elif not isinstance(prot, int): error("Argument error. Expected integer, got " + str(type(prot))) return None if( constraint is None ): constraint = Constraint() if( assertion is None ): assertion = Assertion() # Set the registers args = [[Arch.n2r('eax'),0x7d],[Arch.n2r('ebx'), addr],[Arch.n2r('ecx'),size], [Arch.n2r('edx'),prot]] chain = popMultiple(args, constraint, assertion, clmax-1, optimizeLen) if( not chain ): verbose("Failed to set registers for the mprotect syscall") return None # Int 0x80 int80_gadgets = search(QueryType.INT80, None, None, constraint, assertion) if( not int80_gadgets ): verbose("Failed to find an 'int 80' gadget") return None else: chain.addChain(int80_gadgets[0]) verbose("Success") return chain
def parse_keep_regs(string): """ Parses a 'keep registers' string into a list of register uids Input: a string of format like "rax,rcx,rdi" Output if valid string (True, list) where list = [1, 3, 4] (R1 is rax, R3 is RCX, ... ) Output if invalid string (False, error_message) """ user_keep_regs = string.split(',') keep_regs = set() for reg in user_keep_regs: if (reg in Arch.regNameToNum): keep_regs.add(Arch.n2r(reg)) else: return (False, "Error. '{}' is not a valid register".format(reg)) return (True, list(keep_regs))
def getSemantics(self, value): if( isinstance( value, Expr)): return self.semantics.get(value) else: # Try to translate into a reg if( isinstance(value, int)): num = value elif( value in Arch.regNameToNum ): num = Arch.n2r(value) else: return [] # Return corresponding if present if( num in self.graph.lastMod ): reg = SSAReg(num, self.graph.lastMod[num]) return self.semantics.get(reg) # Or return the same else: return [SPair(SSAExpr(num, 0), CTrue())]
def possiblePopOffsets(self, reg, constraint, assertion): global gadgets res = dict() lookUp = self.types[QueryType.MEMtoREG][reg]\ .registers.get(Arch.n2r(Arch.currentArch.sp), None) if (lookUp is None): return dict() for cst in lookUp.values: tmp = [] for i in range(0, len(lookUp.values[cst])): gadget = gadgets[lookUp.values[cst][i]] (status, conds) = constraint.verify(gadget) if (status): # if yes, check if the assertion verifies the constraint remaining = assertion.filter( conds + [lookUp.preConditions[cst][i]]) if (not remaining): tmp.append(gadget) res[cst] = tmp return res
def __init__(self, addr_list, raw): """ addr_list = list of addresses of the gadget (if duplicate gadgets) raw = raw string of gadget asm """ # Check the type of the gadget # Check for 'int 0x80' gadgets if( raw == '\xcd\x80' and Arch.currentIsIntel()): self.type = GadgetType.INT80 self.asmStr = 'int 0x80' self.hexStr = '\\xcd\\x80' self.addrList = addr_list self.nbInstr = self.nbInstrREIL = 1 self.semantics = Semantics() return # Check for 'syscall' gadgets elif( raw == '\x0f\x05' and Arch.currentIsIntel()): self.type = GadgetType.SYSCALL self.asmStr = 'syscall' self.hexStr = '\\x0f\\x05' self.addrList = addr_list self.nbInstr = self.nbInstrREIL = 1 self.semantics = Semantics() return # Translate raw assembly into REIL # Then compute the Graph and its semantics try: (irsb, ins) = Arch.currentArch.asmToREIL(raw) except Arch.ArchException as e: raise GadgetException(str(e)) try: self.graph = REILtoGraph(irsb) self.semantics = self.graph.getSemantics() except GraphException as e: raise GadgetException("(In {}) - ".format('; '.join(str(i) for i in ins)) + str(e)) self.type = GadgetType.REGULAR # Possible addresses self.addrList = addr_list # String representations self.asmStr = '; '.join(str(i) for i in ins) self.hexStr = '\\x' + '\\x'.join("{:02x}".format(ord(c)) for c in raw) # Length of the gadget self.nbInstr = len(ins) self.nbInstrREIL = len(irsb) # List of modified registers # And of memory-read accesses self._modifiedRegs = [] self._memoryReads = [] for reg_num in list(set([reg.num for reg in self.semantics.registers.keys()])): # Check if there is an empty semantics if( not self.getSemantics(reg_num)): #self.semantics.registers.pop(reg) log("Gadget ({}) : empty semantics for {}"\ .format(self.asmStr, Arch.r2n(reg_num))) self._modifiedRegs.append(reg_num) continue # Get modified reg if ((SSAExpr(reg_num,0) != self.getSemantics(reg_num)[0].expr) ): self._modifiedRegs.append(reg_num) # Get memory reads for pair in self.getSemantics(reg_num): self._memoryReads += [m[0] for m in pair.expr.getMemAcc()] self._modifiedRegs = list(set(self._modifiedRegs)) # SP Increment if( self.type != GadgetType.REGULAR ): self.spInc = None else: sp_num = Arch.spNum() if( not sp_num in self.graph.lastMod ): self.spInc = 0 else: sp = SSAReg(sp_num, self.graph.lastMod[sp_num]) if( len(self.semantics.get(sp)) == 1 ): (isInc, inc) = self.semantics.get(sp)[0].expr.isRegIncrement(sp_num) if( isInc ): self.spInc = inc else: self.spInc = None else: self.spInc = None # Return type self.retType = RetType.UNKNOWN self.retValue = None if( self.type == GadgetType.REGULAR ): ip_num = Arch.n2r(Arch.currentArch.ip) ip = SSAReg(ip_num, self.graph.lastMod[ip_num]) sp_num = Arch.n2r(Arch.currentArch.sp) if( self.spInc != None ): for p in self.semantics.get(ip): if( p.cond.isTrue()): if( isinstance(p.expr, MEMExpr)): addr = p.expr.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 - (Arch.currentArch.octets)) ): self.retType = RetType.RET self.retValue = p.expr elif( isinstance(p.expr, SSAExpr )): self.retValue = p.expr # Try to detect gadgets ending by 'call' if( ins[-1]._mnemonic[:4] == "call"): self.retType = RetType.CALL else: self.retType = RetType.JMP
def build_mprotect32(addr, size, prot=7, constraint=None, assertion=None, clmax=SYSCALL_LMAX, optimizeLen=False): """ Call mprotect from X86 arch Args must be on the stack: int mprotect(void *addr, size_t len, int prot) args must be in registers (ebx, ecx, edx) eax must be 0x7d = 125 """ # Check args if not isinstance(addr, int): error("Argument error. Expected integer, got " + str(type(addr))) return None elif not isinstance(size, int): error("Argument error. Expected integer, got " + str(type(size))) return None elif not isinstance(prot, int): error("Argument error. Expected integer, got " + str(type(prot))) return None if (constraint is None): constraint = Constraint() if (assertion is None): assertion = Assertion() # Check if we have the function ! verbose("Trying to call mprotect() function directly") func_call = build_call('mprotect', [addr, size, prot], constraint, assertion, clmax=clmax, optimizeLen=optimizeLen) if (not isinstance(func_call, str)): verbose("Success") return func_call else: if (not constraint.chainable.ret): verbose("Coudn't call mprotect(), try direct syscall") else: verbose("Couldn't call mprotect() and return to ROPChain") return None # Otherwise do syscall directly # Set the registers args = [[Arch.n2r('eax'), 0x7d], [Arch.n2r('ebx'), addr], [Arch.n2r('ecx'), size], [Arch.n2r('edx'), prot]] chain = popMultiple(args, constraint, assertion, clmax - 1, optimizeLen) if (not chain): verbose("Failed to set registers for the mprotect syscall") return None # Int 0x80 int80_gadgets = search(QueryType.INT80, None, None, constraint, assertion) if (not int80_gadgets): verbose("Failed to find an 'int 80' gadget") return None else: chain.addChain(int80_gadgets[0]) verbose("Success") return chain
def build_syscall_Linux(syscall, arg_list, arch_bits, constraint=None, assertion=None, clmax=SYSCALL_LMAX, optimizeLen=False): """ arch_bits = 32 or 64 :) """ # Check args if (syscall.nb_args() != len(arg_list)): error("Error. Expected {} arguments, got {}".format( len(syscall.arg_types), len(arg_list))) return None # Check args length for i in range(0, len(arg_list)): if (not verifyArgType(arg_list[i], syscall.arg_types[i])): error("Argument error for '{}': expected '{}', got '{}'".format( arg_list[i], syscall.arg_types[i], type(arg_list[i]))) return None # Check constraint and assertion if (constraint is None): constraint = Constraint() if (assertion is None): assertion = getBaseAssertion() # Check if we have the function ! verbose("Trying to call {}() function directly".format(syscall.def_name)) func_call = build_call(syscall.function(), arg_list, constraint, assertion, clmax=clmax, optimizeLen=optimizeLen) if (not isinstance(func_call, str)): verbose("Success") return func_call else: if (not constraint.chainable.ret): verbose("Coudn't call {}(), try direct syscall".format( syscall.def_name)) else: verbose("Couldn't call {}() and return to ROPChain".format( syscall.def_name)) return None # Otherwise do syscall directly # Set the registers args = [(Arch.n2r(x[0]), x[1]) for x in zip(syscall.arg_regs, arg_list) + syscall.syscall_arg_regs ] chain = popMultiple(args, constraint, assertion, clmax - 1, optimizeLen=optimizeLen) if (not chain): verbose("Failed to set registers for the mprotect syscall") return None # Int 0x80 if (arch_bits == 32): syscall_gadgets = search(QueryType.INT80, None, None, constraint, assertion) # syscall elif (arch_bits == 64): syscall_gadgets = search(QueryType.SYSCALL, None, None, constraint, assertion) if (not syscall_gadgets): verbose("Failed to find an 'int 0x80' OR 'syscall' gadget") return None else: chain.addChain(syscall_gadgets[0]) verbose("Success") return chain