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 popMultiple(args, constraint=None, assertion=None, clmax=None, optimizeLen=False): """ args is a list of pairs (reg, value) reg is a reg UID value is an int Creates a chain that pops values into regs """ if (clmax <= 0): return None if (constraint is None): constr = Constraint() else: constr = constraint if (assertion is None): a = Assertion() else: a = assertion perms = itertools.permutations(args) for perm in perms: clmax_tmp = clmax res = ROPChain() constr_tmp = constr for arg in perm: if (optimizeLen): pop = search_optimize_len(QueryType.CSTtoREG, arg[0], arg[1], constr_tmp, a, n=1, clmax=clmax_tmp) else: pop = search(QueryType.CSTtoREG, arg[0], arg[1], constr_tmp, a, n=1, clmax=clmax_tmp) if (not pop): break else: clmax_tmp -= len(pop[0]) # If Reached max length, exit if (clmax_tmp < 0): pop = None break else: res.addChain(pop[0]) constr_tmp = constr_tmp.add(RegsNotModified([arg[0]])) if (pop): return res return None
def CSTtoMEM_write(arg1, cst, constraint, assertion, n=1, clmax=LMAX): """ reg <- cst mem(arg2) <- reg """ if (clmax <= 0): return [] res = [] addr_reg = arg1[0] addr_cst = arg1[1] # 1. First strategy (direct) # reg <- cst # mem(arg1) <- reg for reg in range(0, Arch.ssaRegCount): if (reg == Arch.ipNum() or reg == Arch.spNum() or reg == addr_reg): continue # Find reg <- cst # maxdepth 3 or it's too slow cst_to_reg_chains = search(QueryType.CSTtoREG, reg, cst, constraint.add(RegsNotModified([addr_reg])), assertion, n, clmax - 1, maxdepth=3) if (not cst_to_reg_chains): continue # Search for mem(arg1) <- reg # We get all reg2,cst2 s.t mem(arg1) <- reg2+cst2 possible_mem_writes = DBPossibleMemWrites(addr_reg, addr_cst, constraint, assertion, n=1) # 1.A. Ideally we look for reg2=reg and cst2=0 (direct_writes) possible_mem_writes_reg = possible_mem_writes.get(reg) if (possible_mem_writes_reg): direct_writes = possible_mem_writes[reg].get(0, []) else: direct_writes = [] padding = constraint.getValidPadding(Arch.octets()) for write_gadget in direct_writes: for cst_to_reg_chain in cst_to_reg_chains: # Pad the gadgets write_chain = ROPChain([write_gadget]) for i in range(0, write_gadget.spInc - Arch.octets(), Arch.octets()): write_chain.addPadding(padding) full_chain = cst_to_reg_chain.addChain(write_chain, new=True) if (len(full_chain) <= clmax): res.append(full_chain) if (len(res) >= n): return res # 1.B. return res
def STRtoMEM_write(string, addr, constraint, assertion, limit=None, lmax=STR_TO_MEM_LMAX, addr_str=None, hex_info=False): """ WRITE STRATEGY Copy the string using mem(XXX) <- YYY gadgets """ if (not addr_str): addr_str = hex(addr) # We decompose the string in substrings to be copied substrings_addr = find_best_valid_writes(addr, string, constraint, limit) if (substrings_addr is None): return (None, None) # Build chain res = ROPChain() offset = 0 for (substring_addr, substring_val) in substrings_addr: substring_info = "(" + string_bold("Substring in int") + hex( substring_val) + ")" write_chain = store_constant_address(QueryType.CSTtoMEM, substring_addr, substring_val, constraint, assertion, clmax=lmax - len(res), optimizeLen=True) if (write_chain): res.addChain(write_chain) else: verbose("Coudln't find suitable memory write ropchain") return (None, None) return (substrings_addr[0][0], res)
def _CSTtoREG_pop(reg, cst, constraint, assertion, n=1, clmax=LMAX, comment=None): """ Returns a payload that puts cst into register reg by poping it from the stack """ # Test clmax if (clmax <= 0): return [] # Test n if (n < 1): return [] # Check if the cst is incompatible with the constraint if (not constraint.badBytes.verifyAddress(cst)): return [] if (not comment): comment = "Constant: " + string_bold("0x{:x}".format(cst)) # Direct pop from the stack res = [] if (reg == Arch.ipNum()): constraint2 = constraint.remove([CstrTypeID.CHAINABLE]) else: constraint2 = constraint.add(Chainable(ret=True)) possible = DBPossiblePopOffsets(reg, constraint2, assertion) for offset in sorted(filter(lambda x: x >= 0, possible.keys())): # If offsets are too big to fit in the lmax just break if (offset > clmax * Arch.octets()): break # Get possible gadgets possible_gadgets = [g for g in possible[offset]\ if g.spInc >= Arch.octets() \ and g.spInc - Arch.octets() > offset \ and (g.spInc/Arch.octets()-1) <= clmax] # Test if padding is too much for clmax # Pad the gadgets padding = constraint.getValidPadding(Arch.octets()) for gadget in possible_gadgets: chain = ROPChain([gadget]) for i in range(0, gadget.spInc - Arch.octets(), Arch.octets()): if (i == offset): chain.addPadding(cst, comment) else: chain.addPadding(padding) if (len(chain) <= clmax): res.append(chain) if (len(res) >= n): return res return res
def _REGtoREG_increment(arg1, arg2, constraint, assertion, record, n=1, clmax=LMAX): """ Perform REG1 <- REG2+CST with REG1<-REG2; REG1 += CST """ # Test clmax if (clmax <= 0): return [] # Find possible increments gadgets for arg1 possible_inc = DBPossibleInc(arg1, constraint, assertion) print("possible inc " + str(possible_inc)) # Combine increments to get the constant combination = combine_increments(possible_inc.keys(), arg2[1]) print("combination : " + str(combination)) if (not combination): return [] nb = reduce(lambda x, y: x * y, [len(possible_inc[inc]) for inc in combination]) # Translate increments into gadgets inc_gadgets = [possible_inc[inc] for inc in combination] print("inc_gadgets " + str(inc_gadgets)) # And Create full ropchains res = inc_gadgets[0] for gadgets in inc_gadgets[1:]: res = product(res, gadgets) print("res : " + str(res)) res = [ROPChain(c) for c in res] print("res : " + str(res)) if (arg1 != arg2[0]): # Get gadgets REG1 <- REG2 arg2_to_arg1 = search(QueryType.REGtoREG, arg1, (arg2[0],0), \ constraint, assertion, record, n/nb + 1) res = [ chain.addChain(c, new=True) for chain in arg2_to_arg1 for c in res ] return res
def _basic(qtype, arg1, arg2, env, n=1, enablePreConds=False): """ Search for gadgets basic method ( without chaining ) Direct Database check """ if (env.getLmax() <= 0): return FailRecord(lmax=True) if (env.getNoPadding()): maxSpInc = None else: maxSpInc = env.getLmax() * Arch.octets() # Check for special gadgets if (qtype == QueryType.INT80 or qtype == QueryType.SYSCALL): gadgets = DBSearch(qtype, arg1, arg2, env.getConstraint(), env.getAssertion(), n=n, maxSpInc=maxSpInc) res = [ROPChain().addGadget(g) for g in gadgets] return res # Check if the type is IP <- ... # In this case we remove the CHAINABLE constraint which makes no sense if (arg1 == Arch.ipNum()): constraint2 = env.getConstraint().remove([CstrTypeID.CHAINABLE]) else: constraint2 = env.getConstraint() # Check to add assertions when looking for Memory gadgets if (qtype == QueryType.CSTtoMEM or qtype == QueryType.REGtoMEM): assertion2 = env.getAssertion().add( RegsNoOverlap([(arg1[0], Arch.spNum())])) else: assertion2 = env.getAssertion() # Regular gadgets # maxSpInc -> +1 because we don't count the ret but -1 because the gadget takes one place gadgets = DBSearch(qtype, arg1, arg2, constraint2, assertion2, n, enablePreConds=enablePreConds, maxSpInc=maxSpInc) if (enablePreConds): return [(ROPChain().addGadget(g[0]), g[1]) for g in gadgets] elif (env.getNoPadding()): return [ROPChain().addGadget(g) for g in gadgets] else: res = [] padding = constraint2.getValidPadding(Arch.octets()) for g in gadgets: chain = ROPChain().addGadget(g) # Padding the chain if possible if (g.spInc > 0): for i in range(0, g.spInc / Arch.octets() - 1): chain.addPadding(padding) # Adding to the result res.append(chain) if (len(res) == 0): return FailRecord() else: return res
def store_constant_address(qtype, cst_addr, value, constraint=None, assertion=None, clmax=None, optimizeLen=False): """ Does a XXXtoMEM kind of query BUT the memory address is a simple constant ! Expected qtypes are only XXXtoMEM cst_addr is the store address value is the value to store, a single cst or a couple (reg,cst) """ if (clmax is None): clmax = STORE_CONSTANT_ADDRESS_LMAX elif (clmax <= 0): return None if (constraint is None): constr = Constraint() else: constr = constraint if (assertion is None): a = Assertion() else: a = assertion # Tranform the query type if (qtype == QueryType.CSTtoMEM): qtype2 = QueryType.CSTtoREG elif (qtype == QueryType.REGtoMEM): qtype2 = QueryType.REGtoREG elif (qtype == QueryType.MEMtoREG): qtype2 = QueryType.MEMtoREG else: raise Exception( "Query type {} should not appear in this function!".format(qtype)) tried_values = [] tried_cst_addr = [] best = None # If optimizeLen shortest = clmax # Shortest ROPChain found if optimizeLen ;) for ((addr_reg, addr_cst), (reg,cst), gadget) in \ sorted(DBAllPossibleWrites(constr.add(Chainable(ret=True)), a), \ key=lambda x: 0 if (x[1] == value) else 1) : # DOn't use rip or rsp... if( reg == Arch.ipNum() or reg == Arch.spNum()\ or addr_reg == Arch.ipNum() or addr_reg == Arch.spNum()): continue res = None # Check if directly the registers we want to write ;) value_is_reg = False value_to_reg = [] addr_to_reg = [] if ((reg, cst) == value): value_to_reg = [ROPChain()] value_is_reg = True # adapt value if (not isinstance(value, tuple)): adjusted_value = value - cst else: adjusted_value = (value[0], value[1] - cst) adjusted_cst_addr = cst_addr - addr_cst # Get spInc gadget_paddingLen = (gadget.spInc / Arch.octets()) - 1 # Check if tried before if ((reg, cst) in tried_values): continue elif ((addr_reg, addr_cst) in tried_cst_addr): continue ### Try to do reg first then addr_reg # Try to put the value into reg clmax2 = shortest - gadget_paddingLen - 1 if (not value_is_reg): value_to_reg = search(qtype2, reg, adjusted_value, constr, a, clmax=clmax2, n=1, optimizeLen=optimizeLen) if (not value_to_reg): tried_values.append((reg, cst)) continue else: clmax2 = clmax2 - len(value_to_reg[0]) # Try to put the cst_addr in addr_reg addr_to_reg = search(QueryType.CSTtoREG, addr_reg, adjusted_cst_addr, constr.add(RegsNotModified([reg])), a, clmax=clmax2, n=1, optimizeLen=optimizeLen) if (addr_to_reg): # If we found a solution # Combine them and return # Padd the gadget res = value_to_reg[0].addChain(addr_to_reg[0]).addGadget(gadget) if (gadget.spInc > 0): padding_value = constr.getValidPadding(Arch.octets()) res = res.addPadding(padding_value, n=(gadget.spInc / Arch.octets()) - 1) if (optimizeLen): if (best): best = min(best, res) else: best = res shortest = len(best) else: return res ### Try to do addr_reg first and then reg clmax2 = shortest - gadget_paddingLen - 1 # Try to put the cst_addr in addr_reg addr_to_reg = search(QueryType.CSTtoREG, addr_reg, adjusted_cst_addr, constr, a, clmax=clmax2, n=1, optimizeLen=optimizeLen) if (not addr_to_reg): tried_cst_addr.append((addr_reg, addr_cst)) continue else: clmax2 = clmax2 - len(addr_to_reg[0]) # Try to put the value into reg if (not value_is_reg): value_to_reg = search(qtype2, reg, adjusted_value, constr.add(RegsNotModified([addr_reg])), a, clmax=clmax2, n=1, optimizeLen=optimizeLen) if (value_to_reg): # If we found a solution # Combine them and return # Padd the gadget res = addr_to_reg[0].addChain(value_to_reg[0]).addGadget(gadget) if (gadget.spInc > 0): padding_value = constr.getValidPadding(Arch.octets()) res = res.addPadding(padding_value, n=(gadget.spInc / Arch.octets()) - 1) if (optimizeLen): if (best): best = min(best, res) else: best = res shortest = len(best) else: return res # 5 = two pops for addr_reg and reg + 1 for the write gadget # So since 5 is the shortest possible with two pops we can return # We can have < 5 if reg is already equal to 'value' argument # But we try this case first (see sorted()) when getting possibleWrites ;) if (((not optimizeLen) or (not value_is_reg)) and (not best is None) and len(best) <= 5): return best elif (optimizeLen and (not best is None) and len(best) <= 3): return best return best
def _basic(qtype, arg1, arg2, constraint, assertion, n=1, clmax=LMAX, noPadding=False): """ Search for gadgets basic method ( without chaining ) Direct Database check """ # Test clmax if (clmax <= 0): return [] if (not noPadding): maxSpInc = clmax * Arch.octets() else: maxSpInc = None # Check for special gadgets if (qtype == QueryType.INT80 or qtype == QueryType.SYSCALL): gadgets = DBSearch(qtype, arg1, arg2, constraint, assertion, n=1, maxSpInc=maxSpInc) res = [ROPChain().addGadget(g) for g in gadgets] return res # Check if the type is IP <- ... # In this case we remove the CHAINABLE constraint which makes no sense if (arg1 == Arch.ipNum()): constraint2 = constraint.remove([CstrTypeID.CHAINABLE]) else: constraint2 = constraint # Check to add assertions when looking for Memory gadgets if (qtype == QueryType.CSTtoMEM or qtype == QueryType.REGtoMEM): assertion2 = assertion.add(RegsNoOverlap([(arg1[0], Arch.spNum())])) else: assertion2 = assertion # Regular gadgets # maxSpInc -> +1 because we don't count the ret but -1 because the gadget takes one place gadgets = DBSearch(qtype, arg1, arg2, constraint2, assertion2, n, maxSpInc=maxSpInc) if (noPadding): return [ROPChain().addGadget(g) for g in gadgets] else: res = [] padding = constraint2.getValidPadding(Arch.currentArch.octets) for g in gadgets: chain = ROPChain().addGadget(g) # Padding the chain if possible if (g.spInc > 0): for i in range(0, g.spInc / Arch.octets() - 1): chain.addPadding(padding) # Adding to the result res.append(chain) return res
def STRtoMEM_memcpy(string, addr, constraint, assertion, limit=None, lmax=STR_TO_MEM_LMAX, addr_str=None, hex_info=False): """ MEMCPY STRATEGY Copy the string using memcpy function """ if (not addr_str): addr_str = hex(addr) # Getting strcpy function (func_name, func_addr) = getFunctionAddress('memcpy') if (not func_addr): verbose('Could not find memcpy function') return (None, None) elif (not constraint.badBytes.verifyAddress(func_addr)): verbose("memcpy address ({}) contains bad bytes".format( hex(func_addr))) return (None, None) # We decompose the string in substrings to be copied substrings_addr = findBytes(string, badBytes=constraint.getBadBytes()) if (not substrings_addr): return (None, None) # Find delivery address substr_lengthes = [len(substr[1]) for substr in substrings_addr] if (not limit is None): custom_stack = find_closest_base_fake_stack_address( addr, limit, substr_lengthes, constraint) if (custom_stack is None): verbose("Couldn't write string in memory because of bad bytes") return (None, None) else: custom_stack = find_closest_base_fake_stack_address( addr, addr + sum(substr_lengthes), substr_lengthes, constraint) if (custom_stack is None): verbose("Couldn't write string in memory because of bad bytes") return (None, None) if (custom_stack != addr): addr_str = hex(custom_stack) # Build chain res = ROPChain() offset = 0 saved_custom_stack = custom_stack for (substring_addr, substring_str) in substrings_addr: if (hex_info): substring_info = "'" + '\\x' + '\\x'.join( ["%02x" % ord(c) for c in substring_str]) + "'" else: substring_info = "'" + substring_str + "'" comment3 = "Arg3: " + string_ropg(str(len(substring_str))) comment2 = "Arg2: " + string_ropg(substring_info) comment1 = "Arg1: " + string_ropg("{} + {}".format(addr_str, offset)) func_call = build_call(func_name, [custom_stack, substring_addr, len(substring_str)],\ constraint, assertion, [comment1, comment2, comment3], optimizeLen=True) if (isinstance(func_call, str)): verbose("memcpy: " + func_call) return (None, None) res.addChain(func_call) if (len(res) > lmax): return (None, None) # Adjust custom_stack = custom_stack + len(substring_str) offset = offset + len(substring_str) return (saved_custom_stack, res)
def build_call(funcName, funcArgs, constraint, assertion): # 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'))) # Find a gadget for the fake return address offset = len( funcArgs) * 8 - 8 # Because we do +8 at the beginning of the loop skip_args_chains = [] i = 4 while (i > 0 and (not skip_args_chains)): offset += 8 skip_args_chains = search(QueryType.MEMtoREG, Arch.ipNum(), \ (Arch.spNum(),offset), constraint, assertion, n=1) i -= 1 if (not skip_args_chains): return "Couldn't build ROP-Chain" skip_args_chain = skip_args_chains[0] # Build the ropchain with the arguments args_chain = ROPChain() arg_n = len(funcArgs) for arg in reversed(funcArgs): if (isinstance(arg, int)): args_chain.addPadding(arg, comment="Arg{}: {}".format( arg_n, string_ropg(hex(arg)))) arg_n -= 1 else: return "Type of argument '{}' not supported yet :'(".format(arg) # Build call chain (function address + fake return address) call_chain = ROPChain() call_chain.addPadding(funcAddr, comment=string_ropg(funcName2)) skip_args_addr = int( validAddrStr(skip_args_chain.chain[0], constraint.getBadBytes(), Arch.bits()), 16) call_chain.addPadding(skip_args_addr, comment="Address of: " + string_bold(str(skip_args_chain.chain[0]))) return call_chain.addChain(args_chain)
def STRtoMEM_strcpy(string, addr, constraint, assertion, limit=None, lmax=STR_TO_MEM_LMAX, addr_str=None, hex_info=False): """ STRCPY STRATEGY Copy the string using strcpy function """ if (not addr_str): addr_str = hex(addr) # Getting strcpy function (func_name, func_addr) = getFunctionAddress('strcpy') if (not func_addr): verbose('Could not find strcpy function') return (None, None) elif (not constraint.badBytes.verifyAddress(func_addr)): verbose("strcpy address ({}) contains bad bytes".format( hex(func_addr))) return (None, None) # We decompose the string in substrings to be copied substrings_addr = findBytes(string, badBytes=constraint.getBadBytes(), add_null=True) if (not substrings_addr): return (None, None) # Find delivery address substr_lengthes = [len(substr[1]) - 1 for substr in substrings_addr] # -1 becasue strcpy substr_lengthes[-1] += 1 if (not limit is None): custom_stack = find_closest_base_fake_stack_address( addr, limit, substr_lengthes, constraint) if (custom_stack is None): verbose("Couldn't write string in memory because of bad bytes") return (None, None) else: custom_stack = find_closest_base_fake_stack_address( addr, addr + sum(substr_lengthes), substr_lengthes, constraint) if (custom_stack is None): verbose("Couldn't write string in memory because of bad bytes") return (None, None) if (custom_stack != addr): addr_str = hex(custom_stack) # Build chain res = ROPChain() offset = 0 saved_custom_stack = custom_stack for (substring_addr, substring_str) in substrings_addr: if (hex_info): substring_info = '\\x' + '\\x'.join( ["%02x" % ord(c) for c in substring_str]) else: substring_info = substring_str commentStack = "Arg2: " + string_ropg("{} + {}".format( addr_str, offset)) commentSubStr = "Arg1: " + string_ropg(substring_info) func_call = build_call(func_name, [substring_addr, custom_stack], constraint, assertion, [commentSubStr, commentStack], optimizeLen=True) if (isinstance(func_call, str)): verbose("strcpy: " + func_call) return (None, None) else: res.addChain(func_call) if (len(res) > lmax): return (None, None) # Adjust # -1 Because strcpy has a null byte :/ # Except when we INTEND to write a null byte if (substring_str == '\x00'): dec = 0 else: dec = 1 custom_stack = custom_stack + len(substring_str) - dec offset = offset + len(substring_str) - dec return (saved_custom_stack, res)
def build_call_linux86(funcName, funcArgs, constraint, assertion, clmax=None, optimizeLen=False): # 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 if lmax too small if ((1 + len(funcArgs) + (lambda x: 1 if len(x) > 0 else 0)(funcArgs)) > clmax): return "Not enough bytes to call function '{}'".format(funcName) # Find a gadget for the fake return address if (funcArgs): offset = (len(funcArgs) - 1) * Arch.octets( ) # Because we do +octets() at the beginning of the loop skip_args_chains = [] i = 4 # Try 4 more maximum while (i > 0 and (not skip_args_chains)): offset += Arch.octets() skip_args_chains = search(QueryType.MEMtoREG, Arch.ipNum(), \ (Arch.spNum(),offset), constraint, assertion, n=1, optimizeLen=optimizeLen) i -= 1 if (not skip_args_chains): return "Couldn't build ROP-Chain" skip_args_chain = skip_args_chains[0] else: # No arguments skip_args_chain = None # Build the ropchain with the arguments args_chain = ROPChain() arg_n = len(funcArgs) for arg in reversed(funcArgs): if (isinstance(arg, int)): args_chain.addPadding(arg, comment="Arg{}: {}".format( arg_n, string_ropg(hex(arg)))) arg_n -= 1 else: return "Type of argument '{}' not supported yet :'(".format(arg) # Build call chain (function address + fake return address) call_chain = ROPChain() call_chain.addPadding(funcAddr, comment=string_ropg(funcName2)) if (funcArgs): skip_args_addr = int( validAddrStr(skip_args_chain.chain[0], constraint.getBadBytes(), Arch.bits()), 16) call_chain.addPadding(skip_args_addr, comment="Address of: " + string_bold(str(skip_args_chain.chain[0]))) return call_chain.addChain(args_chain)
def popMultiple(args, constraint=None, assertion=None, clmax=None, addr=None, limit=None, optimizeLen=False): """ args is a list of pairs (reg, value) OR a list of triples (reg, value, comment) reg is a reg UID value is an int OR a string addr and limit are used to put strings if args contains strings Creates a chain that pops values into regs """ if (clmax is None): clmax = POP_MULTIPLE_LMAX_PER_REG * len(args) elif (clmax <= 0): return None for arg in args: if (isinstance(arg, str)): clmax += STR_TO_MEM_LMAX if (constraint is None): constr = Constraint() else: constr = constraint if (assertion is None): a = Assertion() else: a = assertion # Get address #Find address for the payload if (not addr): # Get the .bss address addr = getSectionAddress('.bss') if (not addr): return None perms = itertools.permutations(args) for perm in perms: clmax_tmp = clmax res = ROPChain() constr_tmp = constr tmp_addr = addr chains = None for arg in perm: if (len(arg) == 3): comment = arg[2] else: comment = None if (isinstance(arg[1], int)): chains = search(QueryType.CSTtoREG, arg[0], arg[1], constr_tmp, a, n=1, clmax=clmax_tmp, CSTtoREG_comment=comment, optimizeLen=optimizeLen) elif (isinstance(arg[1], str)): (address, str_to_mem) = STRtoMEM(arg[1], tmp_addr, constr_tmp, a, limit=limit, lmax=clmax_tmp, addr_str=comment, hex_info=True, optimizeLen=optimizeLen) if (not str_to_mem): break tmp_addr = address + len(arg) pop = search(QueryType.CSTtoREG, arg[0], address, constr_tmp, a, n=1, clmax=clmax_tmp - len(str_to_mem), optimizeLen=optimizeLen) if (not pop): break chains = [str_to_mem.addChain(pop[0])] else: raise Exception( "UNknown argument type in popMultiple: '{}'".format( type(arg))) if (not chains): break else: clmax_tmp -= len(chains[0]) # If Reached max length, exit if (clmax_tmp < 0): chains = None break else: res.addChain(chains[0]) constr_tmp = constr_tmp.add(RegsNotModified([arg[0]])) if (chains): return res return None
def _CSTtoREG_pop(reg, cst, env, n=1): """ Returns a payload that puts cst into register reg by poping it from the stack """ ID = StrategyType.CSTtoREG_POP ## Test for special cases # Test lmax if (env.getLmax() <= 0): return FailRecord(lmax=True) # Limit number of calls to ... elif (env.nbCalls(ID) >= 99): return FailRecord() # Check if the cst is in badBytes elif (not env.getConstraint().badBytes.verifyAddress(cst)): return FailRecord() # Set env env.addCall(ID) # Get comment if (env.hasComment(ID)): envHadComment = True comment = env.popComment(ID) else: envHadComment = False comment = "Constant: " + string_bold("0x{:x}".format(cst)) ######################## # Direct pop from the stack res = [] res_fail = FailRecord() # Adapt constraint if ip <- cst if (reg != Arch.ipNum()): constraint2 = env.getConstraint().add(Chainable(ret=True)) else: constraint2 = env.getConstraint() possible = DBPossiblePopOffsets(reg, constraint2, env.getAssertion()) for offset in sorted(filter(lambda x: x >= 0, possible.keys())): # If offsets are too big to fit in the lmax just break if (offset > env.getLmax() * Arch.octets()): break # Get possible gadgets possible_gadgets = [g for g in possible[offset]\ if g.spInc >= Arch.octets() \ and g.spInc - Arch.octets() > offset \ and (g.spInc/Arch.octets()-1) <= env.getLmax()] # Test if padding is too much for clmax # Pad the gadgets padding = env.getConstraint().getValidPadding(Arch.octets()) for gadget in possible_gadgets: chain = ROPChain([gadget]) for i in range(0, gadget.spInc - Arch.octets(), Arch.octets()): if (i == offset): chain.addPadding(cst, comment) else: chain.addPadding(padding) if (len(chain) <= env.getLmax()): res.append(chain) if (len(res) >= n): break if (len(res) >= n): break ######################### # Restore env env.removeCall(ID) if (envHadComment): env.pushComment(ID, comment) return res if res else res_fail
def _CSTtoMEM_write(arg1, cst, env, n=1): """ reg <- cst mem(arg2) <- reg """ ID = StrategyType.CSTtoMEM_WRITE ## Test for special cases # Test lmax if (env.getLmax() <= 0): return FailRecord(lmax=True) # Limit number of calls to ... elif (env.nbCalls(ID) >= 99): return FailRecord() # Set env env.addCall(ID) ###################################### res = [] res_fail addr_reg = arg1[0] addr_cst = arg1[1] # 1. First strategy (direct) # reg <- cst # mem(arg1) <- reg for reg in Arch.registers(): if (reg == Arch.ipNum() or reg == Arch.spNum() or reg == addr_reg): continue # Find reg <- cst constraint = env.getConstraint() env.setConstraint(constraint.add(RegsNotModified([addr_reg]))) env.subLmax(1) cst_to_reg_chains = _search(QueryType.CSTtoREG, reg, cst, env, n) env.addLmax(1) env.setConstraint(constraint) if (not cst_to_reg_chains): res_fail.merge(cst_to_reg_chains) continue # Search for mem(arg1) <- reg # We get all reg2,cst2 s.t mem(arg1) <- reg2+cst2 possible_mem_writes = DBPossibleMemWrites(addr_reg, addr_cst, env.getConstraint(), env.getAssertion(), n=1) # 1.A. Ideally we look for reg2=reg and cst2=0 (direct_writes) possible_mem_writes_reg = possible_mem_writes.get(reg) if (possible_mem_writes_reg): direct_writes = possible_mem_writes[reg].get(0, []) else: direct_writes = [] padding = constraint.getValidPadding(Arch.octets()) for write_gadget in direct_writes: for cst_to_reg_chain in cst_to_reg_chains: # Pad the gadgets write_chain = ROPChain([write_gadget]) for i in range(0, write_gadget.spInc - Arch.octets(), Arch.octets()): write_chain.addPadding(padding) full_chain = cst_to_reg_chain.addChain(write_chain, new=True) if (len(full_chain) <= env.getLmax()): res.append(full_chain) if (len(res) >= n): break if (len(res) >= n): break if (len(res) >= n): break # 1.B. Otherwise we try to adjust the cst2 # To be implemented # 2d Strategy: indirect # reg <- arg2 - cst # mem(arg1) <- reg + cst # TO IMPLEMENT IN ANOTHER FUNCTION ! ################### # Restore env env.removeCall(ID) return res if res else res_fail
def STRtoMEM_memcpy(string, addr, constraint, assertion, lmax=STR_TO_MEM_LMAX, addr_str=None, hex_info=False): """ MEMCPY STRATEGY Copy the string using memcpy function """ if (not addr_str): addr_str = "0x" + format(addr, '0' + str(Arch.octets() * 2) + 'x') # Getting strcpy function (func_name, func_addr) = getFunctionAddress('memcpy') if (not func_addr): verbose('Could not find memcpy function') return None elif (not constraint.badBytes.verifyAddress(func_addr)): verbose("memcpy address ({}) contains bad bytes".format( hex(func_addr))) return None # We decompose the string in substrings to be copied substrings_addr = findBytes(string, badBytes=constraint.getBadBytes()) if (not substrings_addr): return None elif (len(substrings_addr) * 5 > lmax): verbose( "Memcpy: ROP-Chain too long (length: {}, available bytes: {}) ". format( len(substrings_addr) * 5 * Arch.octets(), lmax * Arch.octets())) return None # Get a pop-pop-pop-ret gadget pppr_chains = _basic(QueryType.MEMtoREG, Arch.ipNum(), [Arch.spNum(), 3 * Arch.octets()], constraint.add( StackPointerIncrement(4 * Arch.octets())), assertion, clmax=1, noPadding=True) if (not pppr_chains): verbose("Memcpy: Could not find suitable pop-pop-pop-ret gadget") return None pppr_gadget = pppr_chains[0].chain[0] # Get the first gadget # Build chain res = ROPChain() offset = 0 custom_stack = addr for (substring_addr, substring_str) in substrings_addr: if (hex_info): substring_info = "'" + '\\x' + '\\x'.join( ["%02x" % ord(c) for c in substring_str]) + "'" else: substring_info = "'" + substring_str + "'" res.addPadding(func_addr, comment=string_ropg(func_name)) res.addGadget(pppr_gadget) res.addPadding(len(substring_str), comment="Arg3: " + string_ropg(str(len(substring_str)))) res.addPadding(substring_addr, comment="Arg2: " + string_ropg(substring_info)) res.addPadding(custom_stack, comment="Arg1: " + string_ropg("{} + {}".format(addr_str, offset))) # Adjust custom_stack = custom_stack + len(substring_str) offset = offset + len(substring_str) return res