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 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 verify(self, gadget): if( self.ret == self.jmp == self.call == False ): return (True, []) elif( self.ret and gadget.retType == RetType.RET ): return (True, []) elif( self.jmp and gadget.retType == RetType.JMP ): return (True, []) elif( self.call and gadget.retType == RetType.CALL ): return (True, []) # If unknown ret, check if ret possible # Is a ret sometimes possible ? if( self.ret ): for p in gadget.getSemantics(Arch.ipNum()): if( isinstance(p.expr, MEMExpr)): addr = p.expr.addr (isInc, inc) = addr.isRegIncrement(Arch.spNum()) # 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 == (gadget.spInc - (Arch.octets())) ): return (True, [p.cond]) # Or a jump ? if( self.jmp ): for p in gadget.getSemantics(Arch.ipNum()): if( isinstance(p.expr, SSAExpr )): return (True, [p.cond]) return (False, [])
def verify(self, gadget): for addr in gadget.addrList: addrBytes = re.findall('..',format(addr, '0'+str(Arch.octets()*2)+'x')) ok = True for byte in self.bytes: if( byte in addrBytes): ok = False break # No bad bytes found, so valid address if( ok ): return (True, []) return (False, [])
def find_best_valid_writes(addr, string, constraint, limit=None): """ When using the write strategy, can have bad bytes in addresses too... Try adjust it """ def string_into_reg(string): bytes_list = [b for b in string] # Get base value if (Arch.octets() != len(bytes_list)): value = constraint.getValidPadding(Arch.octets() - len(bytes_list)) if (value is None): return None else: value = 0 if (Arch.isLittleEndian()): tmp = 0 for byte in reversed(bytes_list): value = (value << 8) + ord(byte) return value elif (Arch.isBigEndian()): tmp = 0 for byte in bytes_list: tmp = (tmp << 8) + byte return (tmp << (8 * len(bytes_list))) + value else: return None res = [] tmp_addr = addr if (not limit): limit = addr + len(string) + 10 while (tmp_addr + len(string) <= limit): res = [] fail = False offset = 0 while (not fail and offset < len(string)): # Get the next write address ok = False for i in reversed(range(1, Arch.octets() + 1)): if (constraint.badBytes.verifyAddress(tmp_addr + offset + i)): ok = True break if (not ok): fail = True break else: value = string_into_reg(string[offset:i + offset]) res.append((tmp_addr + offset, value)) offset += i if (not fail): return res return None
def string_into_reg(string): bytes_list = [b for b in string] # Get base value if (Arch.octets() != len(bytes_list)): value = constraint.getValidPadding(Arch.octets() - len(bytes_list)) if (value is None): return None else: value = 0 if (Arch.isLittleEndian()): tmp = 0 for byte in reversed(bytes_list): value = (value << 8) + ord(byte) return value elif (Arch.isBigEndian()): tmp = 0 for byte in bytes_list: tmp = (tmp << 8) + byte return (tmp << (8 * len(bytes_list))) + value else: return None
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 _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 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_dshell(shellcode, constraint, assertion, address, limit, lmax): """ Returns a PwnChain() instance or None """ # Build exploit ################# res = PwnChain() #Find address for the payload if (not address): # Get the .bss address # TODO notify("Getting delivery address for shellcode") address = getSectionAddress('.bss') addr_str = ".bss" if (not address): verbose("Couldn't find .bss address") return [] else: addr_str = hex(address) if (not limit): limit = address + Arch.minPageSize() # Deliver shellcode notify("Building chain to copy shellcode in memory") verbose("{}/{} bytes available".format(lmax * Arch.octets(), lmax * Arch.octets())) (shellcode_address, STRtoMEM_chain) = STRtoMEM(shellcode, address, constraint, assertion, limit=limit, lmax=lmax, addr_str=addr_str, hex_info=True, optimizeLen=True) address = shellcode_address addr_str = hex(address) if (not STRtoMEM_chain): verbose("Could not copy shellcode into memory") return None # Building mprotect notify("Building mprotect() chain") # Getting page to make executable # Arg of mprotect MUST be a valid multiple of page size over_page_size = address % Arch.minPageSize() page_address = address - over_page_size length = len(shellcode) + 1 + over_page_size flag = 7 lmax2 = lmax - len(STRtoMEM_chain) verbose("{}/{} bytes available".format(lmax2 * Arch.octets(), lmax * Arch.octets())) if (lmax2 <= 0): return None if (Arch.currentArch == Arch.ArchX86): mprotect_chain = build_mprotect32(page_address, length, flag, constraint.add(Chainable(ret=True)), assertion, clmax=lmax2 - 2, optimizeLen=True) elif (Arch.currentArch == Arch.ArchX64): mprotect_chain = build_mprotect64(page_address, length, flag, constraint.add(Chainable(ret=True)), assertion, clmax=lmax2 - 2, optimizeLen=True) else: mprotect_chain = None verbose("mprotect call not supported for architecture {}".format( Arch.currentArch.name)) return None if (not mprotect_chain): return None verbose("Done") # Jump to shellcode notify("Searching chain to jump to shellcode") verbose("{}/{} bytes available".format( (lmax2 - len(mprotect_chain)) * Arch.octets(), lmax * Arch.octets())) jmp_shellcode_chains = search(QueryType.CSTtoREG, Arch.ipNum(), address, constraint, assertion, clmax=lmax - len(STRtoMEM_chain) - len(mprotect_chain), optimizeLen=True) if (not jmp_shellcode_chains): verbose("Couldn't find a jump to the shellcode") return None verbose("Done") notify("Done") # Build PwnChain res and return res.add(mprotect_chain, "Call mprotect({},{},{})".format(hex(page_address), length, flag)) res.add(STRtoMEM_chain, "Copy shellcode to {}".format(addr_str)) res.add(jmp_shellcode_chains[0], "Jump to shellcode (address {})".format(addr_str)) return res
def _adjust_ret(qtype, arg1, arg2, env, n): """ Search with basic but adjust the bad returns they have """ global LMAX ID = StrategyType.ADJUST_RET ## Test for special cases # Test lmax if (env.getLmax() <= 0): return FailRecord(lmax=True) # Limit number of calls to ... elif (env.nbCalls(ID) >= 2): return FailRecord() # Test for ip # Reason: can not adjust ip if ip is the # target of the query :/ elif (arg1 == Arch.ipNum()): return FailRecord() # Set env env.addCall(ID) saved_adjust_ret = env.getImpossible_adjust_ret().copy() ######################################## res = [] res_fail = FailRecord() padding = env.getConstraint().getValidPadding(Arch.octets()) # Get possible gadgets constraint = env.getConstraint() env.setConstraint(constraint.add(Chainable(jmp=True, call=True))) possible = _basic(qtype, arg1, arg2, env, 10 * n) env.setConstraint(constraint) if (not possible): res_fail.merge(possible) possible = [] # Try to adjust them for chain in possible: g = chain.chain[0] ret_reg = g.retValue.reg.num # Check if we already know that ret_reg can't be adjusted if (env.checkImpossible_adjust_ret(ret_reg)): continue #Check if ret_reg not modified within the gadget elif (ret_reg in g.modifiedRegs()): continue # Check if stack is preserved elif (g.spInc is None): continue # Find adjustment if (g.spInc < 0): offset = -1 * g.spInc padding_length = 0 else: padding_length = g.spInc / Arch.octets() if (g.retType == RetType.JMP): offset = 0 else: offset = Arch.octets() if (isinstance(arg1, int)): arg1_reg = arg1 else: arg1_reg = arg1[0] # Get adjustment gadgets env.setConstraint(constraint.add(RegsNotModified([arg1_reg]))) saved_lmax = env.getLmax() env.setLmax(LMAX) adjust_gadgets = _search(QueryType.MEMtoREG, Arch.ipNum(), \ (Arch.spNum(),offset), env, n=1) env.setConstraint(constraint) env.setLmax(saved_lmax) if (not adjust_gadgets): res_fail.merge(adjust_gadgets) continue else: adjust_addr = int(validAddrStr(adjust_gadgets[0].chain[0],\ constraint.getBadBytes(), Arch.bits()), 16) # Find gadgets to put the gadget address in the jmp/call register if (isinstance(arg2, int)): arg2_reg = arg2 else: arg2_reg = arg2[0] env.setConstraint(constraint.add(RegsNotModified([arg2_reg]))) env.subLmax(1 + padding_length) env.pushComment( StrategyType.CSTtoREG_POP, "Address of " + string_bold(str(adjust_gadgets[0].chain[0]))) adjust = _search(QueryType.CSTtoREG, ret_reg, adjust_addr, env, n=1) env.popComment(StrategyType.CSTtoREG_POP) env.addLmax(1 + padding_length) env.setConstraint(constraint) if (adjust): res.append(adjust[0].addGadget(g).addPadding(padding, n=padding_length)) if (len(res) >= n): break else: # Update the search record to say that reg_ret cannot be adjusted env.addImpossible_adjust_ret(ret_reg) res_fail.merge(adjust) ######################################## # Restore env env.impossible_adjust_ret = saved_adjust_ret env.removeCall(ID) 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 _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 _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 call(args): global OUTPUT, OUTPUT_CONSOLE, OUTPUT_PYTHON # Parsing arguments if( not args): print_help() return OUTPUT = OUTPUT_CONSOLE funcName = None i = 0 seenOutput = False seenFunction = False seenBadBytes = False seenKeepRegs = False seenShortest = False seenLmax = False seenOffset = False clmax = None offset = 0 constraint = Constraint() assertion = getBaseAssertion() while i < len(args): if( args[i] in [OPTION_LIST, OPTION_LIST_SHORT]): func_list = getAllFunctions() print_functions(func_list) return if( args[i] in [OPTION_BAD_BYTES, OPTION_BAD_BYTES_SHORT]): if( seenBadBytes ): error("Error. '" + args[i] + "' option should be used only once") return if( i+1 >= len(args)): error("Error. Missing bad bytes after option '"+args[i]+"'") return seenBadBytes = True (success, res) = parse_bad_bytes(args[i+1]) if( not success ): error(res) return i = i+2 constraint = constraint.add(BadBytes(res)) elif( args[i] in [OPTION_KEEP_REGS, OPTION_KEEP_REGS_SHORT]): if( seenKeepRegs ): error("Error. '" + args[i] + "' option should be used only once") return if( i+1 >= len(args)): error("Error. Missing register after option '"+args[i]+"'") return seenKeepRegs = True (success, res) = parse_keep_regs(args[i+1]) if( not success ): error(res) return i = i+2 constraint = constraint.add(RegsNotModified(res)) elif( args[i] in [OPTION_CALL, OPTION_CALL_SHORT] ): if( not loadedBinary() ): error("Error. You should load a binary before building ROPChains") return elif( seenFunction ): error("Option '{}' should be used only once".format(args[i])) return userInput = '' i +=1 while( i < len(args) and args[i][0] != "-"): userInput += args[i] i += 1 (funcName, funcArgs ) = parseFunction(userInput) if( not funcName): return seenFunction = True elif( args[i] in [OPTION_OUTPUT, OPTION_OUTPUT_SHORT]): if( seenOutput ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing output format after option '"+args[i]+"'") return if( args[i+1] in [OUTPUT_CONSOLE, OUTPUT_PYTHON]): OUTPUT = args[i+1] seenOutput = True i += 2 else: error("Error. Unknown output format: {}".format(args[i+1])) return elif( args[i] in [OPTION_SHORTEST, OPTION_SHORTEST_SHORT]): if( seenShortest ): error("Option '{}' should be used only once".format(args[i])) return seenShortest = True i += 1 elif( args[i] == OPTION_LMAX or args[i] == OPTION_LMAX_SHORT ): if( seenLmax ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing length after option '"+args[i]+"'") return try: clmax = int(args[i+1]) if( clmax < Arch.octets() ): raise Exception() # Convert number of bytes into number of ropchain elements clmax /= Arch.octets() except: error("Error. '" + args[i+1] +"' bytes is not valid") return i += 2 seenLmax = True elif( args[i] == OPTION_OFFSET or args[i] == OPTION_OFFSET_SHORT ): if( seenOffset ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing offset after option '"+args[i]+"'") return try: offset = int(args[i+1]) if( offset < 0 ): raise Exception() except: try: offset = int(args[i+1], 16) if( offset < 0 ): raise Exception() except: error("Error. '" + args[i+1] +"' is not a valid offset") return i += 2 seenOffset = True elif( args[i] in [OPTION_HELP, OPTION_HELP_SHORT]): print_help() return else: error("Error. Unknown option '{}'".format(args[i])) return if( not funcName ): error("Missing function to call") else: # Set offset if( not set_offset(offset) ): error("Error. Your offset is too big :'(") return try: res = build_call(funcName, funcArgs, constraint, assertion, clmax=clmax, optimizeLen=seenShortest) if( isinstance(res, str) ): error(res) else: print_chains([res], "Built matching ROPChain", constraint.getBadBytes()) except Exception, e: reset_offset() raise sys.exc_info()[1], None, sys.exc_info()[2] # Reset offset reset_offset()
def pwn(args): global OUTPUT, OUTPUT_CONSOLE, OUTPUT_PYTHON global EXPLOIT_LMAX if (not args): print_help() return # Check if gadgets have been loaded in the DB if (not Database.gadgets): error("Oops. You have to load gadgets before pwning ;) ") return # Set default output format OUTPUT = OUTPUT_CONSOLE clmax = EXPLOIT_LMAX seenOutput = False seenLmax = False seenBadBytes = False seenVerbose = False constraint = Constraint() assertion = Assertion() subcommand = None i = 0 while i < len(args): # Parse options if (args[i][0] == '-'): if (args[i] in [OPTION_BAD_BYTES, OPTION_BAD_BYTES_SHORT]): if (seenBadBytes): error("Error. '" + args[i] + "' option should be used only once") return if (i + 1 >= len(args)): error("Error. Missing bad bytes after option '" + args[i] + "'") return seenBadBytes = True (success, res) = parse_bad_bytes(args[i + 1]) if (not success): error(res) return i = i + 2 constraint = constraint.add(BadBytes(res)) elif (args[i] == OPTION_LMAX or args[i] == OPTION_LMAX_SHORT): if (seenLmax): error("Error. '" + arg + "' option should be used only once.") return if (i + 1 >= len(args)): error("Error. Missing output format after option '" + arg + "'") return try: clmax = int(args[i + 1]) if (clmax < Arch.octets()): raise Exception() # Convert number of bytes into number of ropchain elements clmax /= Arch.octets() except: error("Error. '" + args[i + 1] + "' bytes is not valid") return i = i + 2 seenLmax = True elif (args[i] in [OPTION_OUTPUT, OPTION_OUTPUT_SHORT]): if (seenOutput): error("Option '{}' should be used only once".format( args[i])) return if (i + 1 >= len(args)): error("Error. Missing output format after option '" + args[i] + "'") return if (args[i + 1] in [OUTPUT_CONSOLE, OUTPUT_PYTHON]): OUTPUT = args[i + 1] seenOutput = True i += 2 else: error("Error. Unknown output format: {}".format(args[i + 1])) return elif (args[i] in [OPTION_HELP, OPTION_HELP_SHORT]): print_help() return elif (args[i] in [OPTION_VERBOSE, OPTION_VERBOSE_SHORT]): seenVerbose = True i += 1 else: error("Error. Unknown option '{}'".format(args[i])) return # Parse a subcommand else: verbose_mode(seenVerbose) payload = None if (args[i] == CMD_DELIVER_SHELLCODE): payload = dshell(args[i + 1:], constraint, assertion, lmax=clmax) else: error("Error. Unknown subcommand : '{}'".format(args[i])) verbose_mode(False) return verbose_mode(False) # Print result if (payload): print( string_bold("\n\tBuilt exploit - {} bytes\n".format( payload.len_bytes))) if (OUTPUT == OUTPUT_CONSOLE): print( payload.strConsole(Arch.bits(), constraint.getBadBytes())) elif (OUTPUT == OUTPUT_PYTHON): print( payload.strPython(Arch.bits(), constraint.getBadBytes())) else: error("Couldn't generate exploit") return # Test parsing result if (subcommand is None): error("Missing subcommand") return
def _adjust_ret(qtype, arg1, arg2, constraint, assertion, n, clmax=LMAX, record=None, comment=""): """ Search with basic but adjust the bad returns they have """ # Test clmax if (clmax <= 0): return [] # Test for ip if (arg1 == Arch.ipNum()): return [] # Test for search record if (record is None): record = SearchRecord() res = [] possible = _basic(qtype, arg1, arg2, \ constraint.add(Chainable(jmp=True, call=True)), assertion, n) padding = constraint.getValidPadding(Arch.currentArch.octets) for chain in possible: g = chain.chain[0] ret_reg = g.retValue.reg.num # Check if we already know that ret_reg can't be adjusted if (record.impossible_AdjustRet.check(ret_reg)): continue #Check if ret_reg not modified within the gadget if (ret_reg in g.modifiedRegs()): continue # Check if stack is preserved if (g.spInc is None): continue # Find adjustment if (g.spInc < 0): offset = -1 * g.spInc padding_length = 0 else: padding_length = g.spInc if (g.retType == RetType.JMP): offset = 0 else: offset = Arch.octets() adjust_gadgets = search(QueryType.MEMtoREG, Arch.ipNum(), \ (Arch.spNum(),offset), constraint.add(RegsNotModified([arg1])), assertion, n=1, record=record) if (not adjust_gadgets): continue else: adjust_addr = int(validAddrStr(adjust_gadgets[0].chain[0],\ constraint.getBadBytes(), Arch.bits()), 16) # Put the gadget address in the register adjust = search(QueryType.CSTtoREG, ret_reg, adjust_addr, \ constraint.add(RegsNotModified([arg2[0]])), assertion, n=1, clmax=clmax-len(chain),record=record,\ comment="Address of "+string_bold(str(adjust_gadgets[0].chain[0]))) if (adjust): res.append(adjust[0].addGadget(g).addPadding(padding, n=padding_length)) if (len(res) >= n): return res else: # Update the search record to say that reg_ret cannot be adjusted record.impossible_AdjustRet.add(ret_reg) return res
def parse_args(args): """ Parse the user supplied arguments to the 'find' function Returns either a tuple (True, GadgetType, x, y, constraint, nb_res, clmax ) Or if not supported or invalid arguments, returns a tuple (False, msg) ---> See parse_user_request() specification for the list of possible tuples and values/types of x and y """ global OUTPUT seenExpr = False seenBadBytes = False seenKeepRegs = False seenOutput = False seenNbResults = False seenLmax = False i = 0 # Argument counter constraint = Constraint() nbResults = 1 # Default 1 result clmax = LMAX # Max length OUTPUT = OUTPUT_CONSOLE while (i < len(args)): arg = args[i] # Look for options if (arg[0] == '-' and arg[1] != '>'): if (seenExpr): return (False, "Error. Options must come before the search request") # bad bytes option if (arg == OPTION_BAD_BYTES or arg == OPTION_BAD_BYTES_SHORT): if (seenBadBytes): return (False, "Error. '" + arg + "' option should be used only once") if (i + 1 >= len(args)): return (False, "Error. Missing bad bytes after option '" + arg + "'") seenBadBytes = True (success, res) = parse_bad_bytes(args[i + 1]) if (not success): return (False, res) i = i + 1 constraint = constraint.add(BadBytes(res)) # Keep regs option elif (arg == OPTION_KEEP_REGS or arg == OPTION_KEEP_REGS_SHORT): if (seenKeepRegs): return (False, "Error. '" + arg + "' option should be used only once.") if (i + 1 >= len(args)): return (False, "Error. Missing registers after option '" + arg + "'") seenKeepRegs = True (success, res) = parse_keep_regs(args[i + 1]) if (not success): return (False, res) i = i + 1 constraint = constraint.add(RegsNotModified(res)) # Output option elif (arg == OPTION_OUTPUT or arg == OPTION_OUTPUT_SHORT): if (seenOutput): return (False, "Error. '" + arg + "' option should be used only once.") if (i + 1 >= len(args)): return (False, "Error. Missing output format after option '" + arg + "'") if (args[i + 1] in [OUTPUT_CONSOLE, OUTPUT_PYTHON]): OUTPUT = args[i + 1] seenOutput = True i = i + 1 else: return (False, "Error. '" + args[i + 1] + "' output format is not supported") # Nb of results option elif (arg == OPTION_NB_RESULTS or arg == OPTION_NB_RESULTS_SHORT): if (seenNbResults): return (False, "Error. '" + arg + "' option should be used only once.") if (i + 1 >= len(args)): return (False, "Error. Missing output format after option '" + arg + "'") try: nbResults = int(args[i + 1]) if (nbResults <= 0): raise Exception() except: return (False, "Error. '" + args[i + 1] + "' is not a valid number") i = i + 1 seenNbResults = True elif (arg == OPTION_LMAX or arg == OPTION_LMAX_SHORT): if (seenLmax): return (False, "Error. '" + arg + "' option should be used only once.") if (i + 1 >= len(args)): return (False, "Error. Missing output format after option '" + arg + "'") try: clmax = int(args[i + 1]) if (clmax < Arch.octets()): raise Exception() # Convert number of bytes into number of ropchain elements clmax /= Arch.octets() except: return (False, "Error. '" + args[i + 1] + "' bytes is not valid") i = i + 1 seenLmax = True # Otherwise Ignore else: return (False, "Error. Unknown option: '{}' ".format(arg)) # If not option it should be a request expr=expr else: if (seenExpr): return (False, "Error. Unexpected extra expression: '" + ' '.join(args[i:]) + "'") else: seenExpr = True parsed_query = parse_query(''.join(args[i:])) if (not parsed_query[0]): return (False, parsed_query[1]) else: i = len(args) i = i + 1 if (not seenExpr): return (False, "Error. Missing specification of gadget to find") else: return parsed_query + (constraint, nbResults, clmax)
def add(self, chain, descr): self.ROPChains.append(chain) self.info.append(descr) self.len_bytes += len(chain) * Arch.octets()
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
def pwn(args): global OUTPUT, OUTPUT_CONSOLE, OUTPUT_PYTHON global EXPLOIT_LMAX if( not args ): print_help() return # Check if gadgets have been loaded in the DB if( not Database.gadgets ): error("Oops. You have to load gadgets before pwning ;) ") return # Set default output format OUTPUT = OUTPUT_CONSOLE clmax = EXPLOIT_LMAX seenOutput = False seenLmax = False seenBadBytes = False seenVerbose = False seenOutfile = False seenPaddingByte = False seenPaddingLen = False constraint = Constraint() assertion = Assertion() subcommand = None outfile = None paddingByteStr = None paddingLen = 0 i = 0 while i < len(args): # Parse options if( args[i][0] == '-' ): if( args[i] in [OPTION_BAD_BYTES, OPTION_BAD_BYTES_SHORT]): if( seenBadBytes ): error("Error. '" + args[i] + "' option should be used only once") return if( i+1 >= len(args)): error("Error. Missing bad bytes after option '"+args[i]+"'") return seenBadBytes = True (success, res) = parse_bad_bytes(args[i+1]) if( not success ): error(res) return i = i+2 constraint = constraint.add(BadBytes(res)) elif( args[i] == OPTION_LMAX or args[i] == OPTION_LMAX_SHORT ): if( seenLmax ): error("Error. '" + arg + "' option should be used only once.") return if( i+1 >= len(args)): error("Error. Missing output format after option '"+arg+"'") return try: clmax = int(args[i+1]) if( clmax < Arch.octets() ): raise Exception() # Convert number of bytes into number of ropchain elements clmax /= Arch.octets() except: error("Error. '" + args[i+1] +"' bytes is not valid") return i = i +2 seenLmax = True elif( args[i] in [OPTION_OUTPUT, OPTION_OUTPUT_SHORT]): if( seenOutput ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing output format after option '"+args[i]+"'") return if( args[i+1] in [OUTPUT_CONSOLE, OUTPUT_PYTHON]): OUTPUT = args[i+1] seenOutput = True i += 2 else: error("Error. Unknown output format: {}".format(args[i+1])) return elif( args[i] in [OPTION_OUTFILE_SHORT, OPTION_OUTFILE] ): if( seenOutfile ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing file name after option '"+args[i]+"'") return seenOutfile = True outfile = args[i+1] i += 2 elif( args[i] in [OPTION_PADDING_BYTE, OPTION_PADDING_BYTE_SHORT] ): if( seenPaddingByte ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing byte after option '"+args[i]+"'") return seenPaddingByte = True try: paddingByte = int(args[i+1], 16) except: error("Error. '{}' is not a valid byte".format(args[i+1])) return if( paddingByte > 0xff ): error("Error. '{}' can't be stored in one byte".format(args[i+1])) return paddingByteStr = "\\x"+format(paddingByte, "02x") i += 2 elif( args[i] in [OPTION_PADDING_LEN, OPTION_PADDING_LEN_SHORT] ): if( seenPaddingLen ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing length after option '"+args[i]+"'") return seenPaddingLen = True try: paddingLen = int(args[i+1], 10) except: try: paddingLen = int(args[i+1], 16) except: error("Error. '{}' is not a valid byte".format(args[i+1])) return if( paddingLen < 0 ): error("Error. Padding length must be > 0") return i += 2 elif( args[i] in [OPTION_HELP, OPTION_HELP_SHORT]): print_help() return elif( args[i] in [OPTION_VERBOSE, OPTION_VERBOSE_SHORT]): seenVerbose = True i += 1 else: error("Error. Unknown option '{}'".format(args[i])) return # Parse a subcommand else: # Before parsing, check arguments # Check arguments if( seenPaddingByte and not seenPaddingLen ): error("Error. You have to specify padding length if you provide a padding byte. See the '-pl,--padding-length' option") return # Parse subcommand verbose_mode(seenVerbose) payload = None if( args[i] == CMD_DELIVER_SHELLCODE ): payload = dshell(args[i+1:], constraint, assertion, lmax=clmax) else: error("Error. Unknown subcommand : '{}'".format(args[i])) verbose_mode(False) return verbose_mode(False) # Print result if( payload == "help"): return elif( not payload is None ): print(string_bold("\n\tBuilt exploit - {} bytes\n".format(payload.len_bytes))) # Normal output if( OUTPUT == OUTPUT_CONSOLE ): payload_string = payload.strConsole(Arch.bits(), constraint.getBadBytes()) elif( OUTPUT == OUTPUT_PYTHON ): payload_string = payload.strPython(Arch.bits(), constraint.getBadBytes(), \ noTab=seenOutfile, paddingByteStr=paddingByteStr, paddingLen=paddingLen) # Output to file if( outfile ): try: f = open(outfile, "w") f.write(remove_colors(payload_string)) f.close() print(string_bold("\n\tWrote payload in file '{}'\n".format(outfile))) except: error("Error. Couldn't not write payload in file '{}'".format(outfile)) else: print(payload_string) else: error("Couldn't generate exploit") return # Test parsing result if( subcommand is None ): error("Missing subcommand") return
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