def main(): # first lets create the base directorty to keep all temporary data try: shutil.rmtree(config.BASETMP) except OSError: pass if os.path.isdir(config.BASETMP) == False: os.mkdir(config.BASETMP) check_env() ## parse the arguments ######### parser = argparse.ArgumentParser(description='VUzzer options') parser.add_argument('-s', '--sut', help='SUT commandline', required=True) parser.add_argument('-i', '--inputd', help='seed input directory (relative path)', required=True) parser.add_argument( '-w', '--weight', help= 'path of the pickle file(s) for BB wieghts (separated by comma, in case there are two) ', required=True) parser.add_argument( '-n', '--name', help= 'Path of the pickle file(s) containing strings from CMP inst (separated by comma if there are two).', required=True) parser.add_argument( '-l', '--libnum', help= 'Nunber of binaries to monitor (only application or used libraries)', required=False, default=1) parser.add_argument( '-o', '--offsets', help= 'base-address of application and library (if used), separated by comma', required=False, default='0x00000000') parser.add_argument('-b', '--libname', help='library name to monitor', required=False, default='') args = parser.parse_args() config.SUT = args.sut config.INITIALD = os.path.join(config.INITIALD, args.inputd) config.LIBNUM = int(args.libnum) config.LIBTOMONITOR = args.libname config.LIBPICKLE = [w for w in args.weight.split(',')] config.NAMESPICKLE = [n for n in args.name.split(',')] config.LIBOFFSETS = [o for o in args.offsets.split(',')] ih = config.PINCMD.index("#") config.PINCMD[ih] = args.libname ################################### config.minLength = get_min_file(config.INITIALD) try: shutil.rmtree(config.KEEPD) except OSError: pass os.mkdir(config.KEEPD) try: os.mkdir("outd") except OSError: pass try: os.mkdir("outd/crashInputs") except OSError: gau.emptyDir("outd/crashInputs") try: os.mkdir("outd/hangs") except OSError: gau.emptyDir("outd/hangs") try: os.mkdir("outd/temp") except: gau.emptyDir("outd/temp") crashHash = [] try: os.mkdir(config.SPECIAL) except OSError: gau.emptyDir(config.SPECIAL) try: os.mkdir(config.INTER) except OSError: gau.emptyDir(config.INTER) ###### open names pickle files gau.prepareBBOffsets() if len(config.cALLBB) > 0: config.BBFORPRUNE = list(config.cALLBB) else: print "[*]: cALLBB is not initialized. something is wrong!!\n" system.exit() if config.PTMODE: pt = simplept.simplept() else: pt = None if config.ERRORBBON == True: gbb, bbb = dry_run() else: gbb = 0 # gau.die("dry run over..") import timing #selftest() noprogress = 0 currentfit = 0 lastfit = 0 config.CRASHIN.clear() stat = open("stats.log", 'w') stat.write("**** Fuzzing started at: %s ****\n" % (datetime.now().isoformat('+'), )) stat.write("**** Initial BB for seed inputs: %d ****\n" % (gbb, )) stat.flush() os.fsync(stat.fileno()) stat.write( "Genaration\t MINfit\t MAXfit\t AVGfit MINlen\t Maxlen\t AVGlen\t #BB\t AppCov\t AllCov\n" ) stat.flush() os.fsync(stat.fileno()) starttime = time.clock() allnodes = set() alledges = set() try: shutil.rmtree(config.INPUTD) #shutil.rmtree(config.BUGD) except OSError: pass shutil.copytree(config.INITIALD, config.INPUTD) # fisrt we get taint of the intial inputs get_taint(config.INITIALD, 1) print "MOst common offsets and values:", config.MOSTCOMMON #gg=raw_input("press enter to continue..") #gau_new.initialFuzz() config.MOSTCOMFLAG = True crashhappend = False filest = os.listdir(config.INPUTD) filenum = len(filest) if filenum < config.POPSIZE: gau.create_files(config.POPSIZE - filenum) if len(os.listdir(config.INPUTD)) != config.POPSIZE: gau.die("something went wrong. number of files is not right!") efd = open(config.ERRORS, "w") gau.prepareBBOffsets() writecache = True genran = 0 bbslide = 10 # this is used to call run_error_BB() functions keepslide = 3 keepfilenum = config.BESTP config.SEENBB.clear() del config.SPECIALENTRY[:] todelete = set() inputs_new_run = [] while True: print "[**] Generation %d\n***********" % (genran, ) #del config.SPECIALENTRY[:] del config.TEMPTRACE[:] del config.BBSEENVECTOR[:] #config.SEENBB.clear() SPECIALCHANGED = False # this is set when at least one new input is added to the config.SPECIAL folder. config.TMPBBINFO.clear() config.TMPBBINFO.update(config.PREVBBINFO) fitnes = dict() execs = 0 config.cPERGENBB.clear() config.GOTSTUCK = False if config.ERRORBBON == True: if genran > config.GENNUM / 5: bbslide = max(bbslide, config.GENNUM / 20) keepslide = max(keepslide, config.GENNUM / 100) keepfilenum = keepfilenum / 2 #config.cPERGENBB.clear() #config.GOTSTUCK=False #if 0< genran < config.GENNUM/5 and genran%keepslide == 0: # copy_files(config.INPUTD,config.KEEPD,keepfilenum) #lets find out some of the error handling BBs if genran > 20 and genran % bbslide == 0: stat.write("\n**** Error BB cal started ****\n") stat.flush() os.fsync(stat.fileno()) #run_error_bb(pt) #copy_files(config.KEEPD,config.INPUTD,len(os.listdir(config.KEEPD))*1/10) #copy_files(config.INITIALD,config.INPUTD,1) if genran == 1 or genran % 10 == 0: #f_log = open('out.txt', 'a') #print >> f_log, genran, inputs_new_run #f_log.close() gau.createBufferOverflowinputs(inputs_new_run) gau.createIntegerOverflowInputs(inputs_new_run) #gau.createMallocInputs(inputs_new_run) inputs_new_run = [] files = os.listdir(config.BUGD) for fl in files: tfl = os.path.join(config.BUGD, fl) iln = os.path.getsize(tfl) args = (config.SUT % tfl).split(' ') progname = os.path.basename(args[0]) #print '' #print 'Input file sha1:', sha1OfFile(tfl) #print 'Going to call:', ' '.join(args) retc = execute_without_analysis(tfl) if retc == None: npath = os.path.join("outd/hangs", fl) shutil.copyfile(tfl, npath) os.remove(tfl) continue #raw_input() execs += 1 #print "** %s: %d"%(fl,fitnes[fl]) if retc < 0 and retc != -2: shutil.copy(tfl, config.INPUTD) files = os.listdir(config.INPUTD) for fl in files: tfl = os.path.join(config.INPUTD, fl) iln = os.path.getsize(tfl) args = (config.SUT % tfl).split(' ') progname = os.path.basename(args[0]) #print '' #print 'Input file sha1:', sha1OfFile(tfl) #print 'Going to call:', ' '.join(args) (bbs, retc) = execute(tfl) if os.path.exists(os.path.join("outd/hangs", fl)): continue if config.BBWEIGHT == True: fitnes[fl] = gau.fitnesCal2(bbs, fl, iln) else: fitnes[fl] = gau.fitnesNoWeight(bbs, fl, iln) #raw_input() execs += 1 SPECIALADDED = False if config.GOTSPECIAL == True: #spinputs=os.listdir("outd/hangs") #if fl in spinputs: # break SPECIALADDED = True SPECIALCHANGED = True todelete.clear() form_bitvector2(bbs, fl, config.BBFORPRUNE, config.SPECIALBITVECTORS) shutil.copy(tfl, config.SPECIAL) config.SPECIALENTRY.append(fl) inputs_new_run.append(fl) for sfl, bitv in config.SPECIALBITVECTORS.iteritems(): if sfl == fl: continue if (config.SPECIALBITVECTORS[fl] & bitv) == bitv: tpath = os.path.join(config.SPECIAL, sfl) os.remove(tpath) todelete.add(sfl) config.SPECIALENTRY.remove(sfl) if sfl in config.TAINTMAP: del config.TAINTMAP[sfl] del config.ANALYSIS_MAP[sfl] if sfl in inputs_new_run: inputs_new_run.remove(sfl) for ele in todelete: del config.SPECIALBITVECTORS[ele] #print "** %s: %d"%(fl,fitnes[fl]) if retc < 0 and retc != -2: print "[*]Error code is %d" % (retc, ) tmpHash = sha1OfFile(config.CRASHFILE) efd.write("%s: %d\n" % (tfl, retc)) efd.flush() os.fsync(efd) config.err_in.append(fl) tnow = datetime.now().isoformat().replace(":", "-") nf = "%s-%s.%s" % (progname, tnow, fl) npath = os.path.join("outd/temp", nf) shutil.copyfile(tfl, npath) if tmpHash not in crashHash: crashHash.append(tmpHash) tnow = datetime.now().isoformat().replace(":", "-") nf = "%s-%s.%s" % (progname, tnow, gau.splitFilename(fl)[1]) npath = os.path.join("outd/crashInputs", nf) shutil.copyfile(tfl, npath) if SPECIALADDED == False: shutil.copy(tfl, config.SPECIAL) #config.SPECIALENTRY.append(fl) #SPECIALADDED=False config.CRASHIN.add(fl) if config.STOPONCRASH == True: #efd.close() crashhappend = True break fitscore = [v for k, v in fitnes.items()] maxfit = max(fitscore) avefit = sum(fitscore) / len(fitscore) mnlen, mxlen, avlen = gau.getFileMinMax(config.INPUTD) print "[*] Done with all input in Gen, starting SPECIAL. \n" #### copy special inputs in SPECIAL directory and update coverage info ### appcov, allcov = gau.calculateCov() tnow = datetime.now().isoformat().replace(":", "-") stat.write( "\t%d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %s\t %s\n" % (genran, min(fitscore), maxfit, avefit, mnlen, mxlen, avlen, len(config.SEENBB), appcov, allcov, config.NUMINPUTS, config.fname, tnow)) stat.flush() os.fsync(stat.fileno()) print "[*] Wrote to stat.log\n" if crashhappend == True: break genran += 1 #this part is to get initial fitness that will be used to determine if fuzzer got stuck. lastfit = currentfit #currentfit=maxfit currentfit = len(config.SEENBB) if currentfit == lastfit: #lastfit-config.FITMARGIN < currentfit < lastfit+config.FITMARGIN: noprogress += 1 else: noprogress = 0 if noprogress > 20: config.GOTSTUCK = True stat.write("Heavy mutate happens now..\n") noprogress = 0 if (genran >= config.GENNUM) and (config.STOPOVERGENNUM == True): break if len(os.listdir(config.SPECIAL)) > 0 and SPECIALCHANGED == True: if len(os.listdir(config.SPECIAL)) < config.NEWTAINTFILES: get_taint(config.SPECIAL) else: try: os.mkdir(config.TAINTTMP) except OSError: gau.emptyDir(config.TAINTTMP) if conditional_copy_files(config.SPECIAL, config.TAINTTMP, config.NEWTAINTFILES) == 0: get_taint(config.TAINTTMP) #print "MOst common offsets and values:", config.MOSTCOMMON #gg=raw_input("press any key to continue..") print "[*] Going for new generation creation.\n" gau.createNextGeneration3(fitnes, genran) #raw_input("press any key...") efd.close() stat.close() libfd_mm.close() libfd.close() endtime = time.clock() print "[**] Totol time %f sec." % (endtime - starttime, ) print "[**] Fuzzing done. Check %s to see if there were crashes.." % ( config.ERRORS, )
def main(): # first lets create the base directorty to keep all temporary data try: shutil.rmtree(config.BASETMP) except OSError: pass if os.path.isdir(config.BASETMP) == False: os.mkdir(config.BASETMP) check_env() ## parse the arguments ######### parser = argparse.ArgumentParser(description='VUzzer options') parser.add_argument( '-s', '--sut', help='SUT commandline with %s as placeholder for SUT input', required=True) parser.add_argument('-i', '--inputd', help='seed input directory (relative path)', required=True) parser.add_argument( '-w', '--weight', help= 'path of the pickle file(s) for BB wieghts (separated by comma, in case there are two) ', required=True) parser.add_argument( '-n', '--name', help= 'Path of the pickle file(s) containing strings from CMP inst (separated by comma if there are two).', required=True) parser.add_argument( '-l', '--libnum', help= 'Nunber of binaries to monitor (only application or used libraries)', required=False, default=1) parser.add_argument( '-o', '--offsets', help= 'base-address of application and library (if used), separated by comma', required=False, default='0x0000000000000000') parser.add_argument('-b', '--libname', help='library name to monitor', required=False, default='') args = parser.parse_args() config.SUT = args.sut config.INITIALD = os.path.join(config.INITIALD, args.inputd) config.LIBNUM = int(args.libnum) config.LIBTOMONITOR = args.libname config.LIBPICKLE = [w for w in args.weight.split(',')] config.NAMESPICKLE = [n for n in args.name.split(',')] config.LIBOFFSETS = [o for o in args.offsets.split(',')] config.LIBS = args.libname #ih=config.BBCMD.index("LIBS=") # this is just to find the index of the placeholder in BBCMD list to replace it with the libname ih = config.BBCMD.index( "#" ) # this is just to find the index of the placeholder in BBCMD list to replace it with the libname #config.BBCMD[ih]="LIBS=%s" % args.libname config.BBCMD[ih] = args.libname ################################### config.minLength = get_min_file(config.INITIALD) try: shutil.rmtree(config.KEEPD) except OSError: pass os.mkdir(config.KEEPD) try: os.mkdir("outd") except OSError: pass try: os.mkdir("outd/crashInputs") except OSError: gau.emptyDir("outd/crashInputs") crashHash = [] try: os.mkdir(config.SPECIAL) except OSError: gau.emptyDir(config.SPECIAL) try: os.mkdir(config.INTER) except OSError: gau.emptyDir(config.INTER) ############################################################################# #let us get the base address of the main executable. ifiles = os.listdir(config.INITIALD) for fl in ifiles: tfl = os.path.join(config.INITIALD, fl) try: f = open(tfl, 'r') f.close() except: gau.die("can not open our own input %s!" % (tfl, )) (ibbs, iretc) = execute(tfl) break # we just want to run the executable once to get its load address imgOffFd = open("imageOffset.txt", 'r') for ln in imgOffFd: if "Main:" in ln: lst = ln.split() break config.LIBOFFSETS[0] = lst[1][:] imgOffFd.close() ############################################################################# ###### open names pickle files gau.prepareBBOffsets() # lets initialize the BBFORPRUNE list from thie cALLBB set. if len(config.cALLBB) > 0: config.BBFORPRUNE = list(config.cALLBB) else: print "[*]: cALLBB is not initialized. something is wrong!!\n" system.exit() if config.PTMODE: pt = simplept.simplept() else: pt = None if config.ERRORBBON == True: gbb, bbb = dry_run() else: gbb = 0 # gau.die("dry run over..") import timing #selftest() noprogress = 0 currentfit = 0 lastfit = 0 config.CRASHIN.clear() stat = open("stats.log", 'w') stat.write("**** Fuzzing started at: %s ****\n" % (datetime.now().isoformat('+'), )) stat.write("**** Initial BB for seed inputs: %d ****\n" % (gbb, )) stat.flush() os.fsync(stat.fileno()) stat.write( "Genaration\t MINfit\t MAXfit\t AVGfit MINlen\t Maxlen\t AVGlen\t #BB\t AppCov\t AllCov\n" ) stat.flush() os.fsync(stat.fileno()) starttime = time.clock() allnodes = set() alledges = set() try: shutil.rmtree(config.INPUTD) except OSError: pass shutil.copytree(config.INITIALD, config.INPUTD) # fisrt we get taint of the intial inputs get_taint(config.INITIALD, 1) #print "MOst common offsets and values:", config.MOSTCOMMON #print "Base address: %s"%config.LIBOFFSETS[0] #raw_input("Press enter to continue..") config.MOSTCOMFLAG = True crashhappend = False filest = os.listdir(config.INPUTD) filenum = len(filest) if filenum < config.POPSIZE: gau.create_files(config.POPSIZE - filenum) if len(os.listdir(config.INPUTD)) != config.POPSIZE: gau.die("something went wrong. number of files is not right!") efd = open(config.ERRORS, "w") gau.prepareBBOffsets() writecache = True genran = 0 bbslide = 100 # this is used to call run_error_BB() functions. currently, i have decided to not call it thus a long wait keepslide = 3 keepfilenum = config.BESTP config.SEENBB.clear() #initialize set of BB seen so far, which is 0 del config.SPECIALENTRY[:] todelete = set( ) #temp set to keep file names that will be deleted in the special folder while True: #print "[**] Generation %d\n***********"%(genran,) del config.TEMPTRACE[:] del config.BBSEENVECTOR[:] SPECIALCHANGED = False # this is set when a config.SPECIAL gets at least one new input per generation. config.TMPBBINFO.clear() config.TMPBBINFO.update(config.PREVBBINFO) fitnes = dict() execs = 0 config.cPERGENBB.clear() config.GOTSTUCK = False if config.ERRORBBON == True: if genran > config.GENNUM / 5: bbslide = max(bbslide, config.GENNUM / 20) keepslide = max(keepslide, config.GENNUM / 100) keepfilenum = keepfilenum / 2 if 0 < genran < config.GENNUM / 5 and genran % keepslide == 0: copy_files(config.INPUTD, config.KEEPD, keepfilenum) #lets find out some of the error handling BBs if genran > 2000 and genran % bbslide == 0: # large number 2000 is to prevent not starting intermediate error BB cal. it is expensive and I am working on it. stat.write("\n**** Error BB cal started ****\n") stat.flush() os.fsync(stat.fileno()) run_error_bb(pt) copy_files(config.KEEPD, config.INPUTD, len(os.listdir(config.KEEPD)) * 1 / 10) #copy_files(config.INITIALD,config.INPUTD,1) files = os.listdir(config.INPUTD) per_gen_fnum = 0 for fl in files: per_gen_fnum += 1 tfl = os.path.join(config.INPUTD, fl) iln = os.path.getsize(tfl) args = (config.SUT % tfl).split(' ') progname = os.path.basename(args[0]) (bbs, retc) = execute(tfl) if per_gen_fnum % 10 == 0: print "[**] Gen: %d. Executed %d of %d.**" % ( genran, per_gen_fnum, config.POPSIZE) if config.BBWEIGHT == True: fitnes[fl] = gau.fitnesCal2(bbs, fl, iln) else: fitnes[fl] = gau.fitnesNoWeight(bbs, fl, iln) #raw_input() execs += 1 #let us prune the inputs(if at all), whose trace is subset of the new input just got executed. SPECIALADDED = False if config.GOTSPECIAL == True: SPECIALCHANGED = True SPECIALADDED = True todelete.clear() form_bitvector2(bbs, fl, config.BBFORPRUNE, config.SPECIALBITVECTORS) shutil.copy(tfl, config.SPECIAL) config.SPECIALENTRY.append(fl) for sfl, bitv in config.SPECIALBITVECTORS.iteritems(): if sfl == fl: continue if (config.SPECIALBITVECTORS[fl] & bitv) == bitv: tpath = os.path.join(config.SPECIAL, sfl) os.remove(tpath) todelete.add(sfl) config.SPECIALENTRY.remove(sfl) if sfl in config.TAINTMAP: del config.TAINTMAP[sfl] for ele in todelete: del config.SPECIALBITVECTORS[ele] if retc < 0 and retc != -2: #print "[*]Error code is %d"%(retc,) efd.write("%s: %d\n" % (tfl, retc)) efd.flush() os.fsync(efd) tmpHash = sha1OfFile(config.CRASHFILE) if tmpHash not in crashHash: crashHash.append(tmpHash) tnow = datetime.now().isoformat().replace(":", "-") nf = "%s-%s.%s" % (progname, tnow, gau.splitFilename(fl)[1]) npath = os.path.join("outd/crashInputs", nf) shutil.copyfile(tfl, npath) if SPECIALADDED == False: shutil.copy(tfl, config.SPECIAL) config.CRASHIN.add(fl) if config.STOPONCRASH == True: #efd.close() crashhappend = True break fitscore = [v for k, v in fitnes.items()] maxfit = max(fitscore) avefit = sum(fitscore) / len(fitscore) mnlen, mxlen, avlen = gau.getFileMinMax(config.INPUTD) print "[*] Done with all input in Gen, starting SPECIAL. \n" appcov, allcov = gau.calculateCov() tnow = datetime.now().isoformat().replace(":", "-") #stat.write("\t%d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %s\n"%(genran,min(fitscore),maxfit,avefit,mnlen,mxlen,avlen,len(config.cPERGENBB),appcov,allcov,tnow)) stat.write("\t%d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %s\n" % (genran, min(fitscore), maxfit, avefit, mnlen, mxlen, avlen, len(config.SEENBB), appcov, allcov, tnow)) stat.flush() os.fsync(stat.fileno()) print "[*] Wrote to stat.log\n" if crashhappend == True: break #lets find out some of the error handling BBs #if genran >20 and genran%5==0: # run_error_bb(pt) genran += 1 #this part is to get initial fitness that will be used to determine if fuzzer got stuck. lastfit = currentfit #currentfit=maxfit currentfit = len(config.SEENBB) if currentfit == lastfit: #lastfit-config.FITMARGIN < currentfit < lastfit+config.FITMARGIN: noprogress += 1 else: noprogress = 0 if noprogress > 20: config.GOTSTUCK = True stat.write("Heavy mutate happens now..\n") noprogress = 0 if (genran >= config.GENNUM) and (config.STOPOVERGENNUM == True): break if len(os.listdir(config.SPECIAL)) > 0 and SPECIALCHANGED == True: if len(os.listdir(config.SPECIAL)) < config.NEWTAINTFILES: get_taint(config.SPECIAL) else: try: os.mkdir(config.TAINTTMP) except OSError: gau.emptyDir(config.TAINTTMP) if conditional_copy_files(config.SPECIAL, config.TAINTTMP, config.NEWTAINTFILES) == 0: get_taint(config.TAINTTMP) #print "MOst common offsets and values:", config.MOSTCOMMON #gg=raw_input("press any key to continue..") print "[*] Going for new generation creation.\n" gau.createNextGeneration3(fitnes, genran) #raw_input("press any key...") efd.close() stat.close() libfd_mm.close() libfd.close() endtime = time.clock() print "[**] Totol time %f sec." % (endtime - starttime, ) print "[**] Fuzzing done. Check %s to see if there were crashes.." % ( config.ERRORS, )
def getOffset(self, file, org): org = list(org) usedFile = file if file not in self.currentTaintMap.keys(): randomFile = self.r.choice(list(self.currentTaintMap.keys())) alloffset = self.currentTaintMap[randomFile][0] usedFile = randomFile else: alloffset = self.currentTaintMap[file][0] if len(alloffset) == 0: gautils.debug_print("[-] Fail to find offset in map") return [] attemps = 10 cmp = -1 # I try to optimize the offset selection by selecting only not used offsets while attemps != 0: cmp = self.r.choice(alloffset) if cmp.offsetsInInput[0] in self.mutationHistory[file]: attemps -= 1 continue else: break gautils.debug_print("[*] Testing offset 0x%x" % (cmp.offsetsInInput[0])) conflictList = self.check_mutation_conflict_from_register_size( cmp.offsetsInInput[0], cmp.cmpSize, file) conflictWithParent = False createdChild = -1 # create the mutation buffer self.currentMutation = self.r.sample(self.r.choice(self.allStrings), cmp.cmpSize) # is there is not offset without conflicts available if len(conflictList) > 0: gautils.debug_print("[-] There is conflict with the parent") createTheChild = random.uniform( 0.1, 1.0) > (1.0 - config.CHILDINPUTCREATIONRANDOMNESS) # creating a child for each conflict if config.HEAVYCHILDINPUTCREATION == True and self.childCount < config.CHILDINPUTMAXSIZE and createTheChild: conflictWithParent = True # create the input child name, inputBuffer = self.create_child_input( file, org, conflictList, cmp) # check is exist and write the file if not self.write_child_input(name, inputBuffer): del self.mutationHistory[name] createdChild = -1 else: gautils.debug_print("[+] Child input created %s" % (name)) createdChild = name else: gautils.debug_print("[+] There is no conflict in parent") # apply the cmp to the child inputs history if len(self.childMap) > 0 and config.HEAVYCHILDINPUTCREATION == True: # childMap will change during this tmpChildMap = self.childMap.copy() for childInput in tmpChildMap: if createdChild != -1 and childInput == createdChild: continue # get conflicts for each child input conflictChildList = self.check_mutation_conflict_from_register_size( cmp.offsetsInInput[0], cmp.cmpSize, childInput) path = os.path.join(config.INPUTD, childInput) # get child input buffer childInputBuffer = list(gautils.readFile(path)) createTheChild = random.uniform( 0.1, 1.0) > (1.0 - config.CHILDINPUTCREATIONRANDOMNESS) # conflict found # NOTE : we still apply cmp to child inputs if there is no conflicts if len( conflictChildList ) > 0 and self.childCount < config.CHILDINPUTMAXSIZE and createTheChild: gautils.debug_print( "[-] There is conflict with child inputs") # create input name # get the extension # TODO : find a better way to do it bn, ext = gautils.splitFilename(usedFile) name = "heavy-child-g%d-%d.%s" % (config.CURRENTGEN, self.childCount, ext) # create the input child inputBuffer = self.create_new_child_from_old( name, childInput, childInputBuffer, conflictChildList, cmp) # check is exist and write the file if not self.write_child_input(name, ''.join(inputBuffer)): del self.mutationHistory[name] else: gautils.debug_print("[+] Child input created %s" % (name)) # no conflict (maybe while self.childCount == config.CHILDINPUTMAXSIZE) elif len(conflictChildList) == 0: gautils.debug_print( "[+] There is no conflict in child inputs") gautils.debug_print("[+] Updating child input %s" % (childInput)) # add the new mutation to the history self.mutationHistory[childInput].update( {cmp.offsetsInInput[0]: [cmp]}) # apply the changes and add it to history childInputBuffer = self.change_bytes_from_cmp( childInputBuffer, childInput, cmp) # check is exist and write the file if not self.write_child_input_update( childInput, ''.join(childInputBuffer)): del self.mutationHistory[childInput][ cmp.offsetsInInput[0]] else: gautils.debug_print("[+] Child input updated %s" % (childInput)) # true only if config.HEAVYCHILDINPUTCREATION == True if conflictWithParent == True: # the parent will not use the mutation because of the conflict return [] else: # TODO : improve self.mutationHistory[file].update({cmp.offsetsInInput[0]: [cmp]}) # we will not use this cmp again self.currentTaintMap[usedFile][0].remove(cmp) # python tricks to not access to cmp function is it None return [cmp]
def main(): banner() # first lets create the base directorty to keep all temporary data try: shutil.rmtree(config.BASETMP) except OSError: pass if os.path.isdir(config.BASETMP) == False: os.mkdir(config.BASETMP) check_env() ## parse the arguments ######### parser = argparse.ArgumentParser(description='VUzzer options') parser.add_argument('-s', '--sut', help='SUT commandline', required=True) parser.add_argument( '-i', '--inputd', help='seed input directory (relative path)', required=True) parser.add_argument( '-w', '--weight', help='path of the pickle file(s) for BB wieghts (separated by comma, in case there are two) ', required=True) parser.add_argument( '-n', '--name', help='Path of the pickle file(s) containing strings from CMP inst (separated by comma if there are two).', required=True) parser.add_argument( '-l', '--libnum', help='Nunber of binaries to monitor (only application or used libraries)', required=False, default=1) parser.add_argument('-o', '--offsets', help='base-address of application and library (if used), separated by comma', required=False, default='0x00000000') parser.add_argument( '-b', '--libname', help='library name to monitor', required=False, default='#') args = parser.parse_args() config.SUT = args.sut config.INITIALD = os.path.join(config.INITIALD, args.inputd) config.LIBNUM = int(args.libnum) config.LIBTOMONITOR = args.libname config.LIBPICKLE = [w for w in args.weight.split(',')] config.NAMESPICKLE = [n for n in args.name.split(',')] config.LIBOFFSETS = [o for o in args.offsets.split(',')] config.LIBS = args.libname # this is just to find the index of the placeholder in BBCMD list to replace it with the libname ih = config.BBCMD.index("LIBS=") config.BBCMD[ih] = "LIBS=%s" % args.libname gau.log_print( "[*] Checking tmps files" ) if config.CLEARSPECIALOUTPUT: gau.delete_special_out_file(config.SPECIALOUTPUT) if path.exists("vuzzerRun.log"): os.remove("vuzzerRun.log") gau.log_print( "[*] Checking directories" ) if config.USEPATTERNDETECTION == True: initPatterns() ################################### afl.clearDir() config.minLength = get_min_file(config.INITIALD) try: shutil.rmtree(config.KEEPD) except OSError: pass os.mkdir(config.KEEPD) try: os.mkdir("outd") except OSError: pass try: os.mkdir("outd/crashInputs") except OSError: gau.emptyDir("outd/crashInputs") crashHash = [] try: os.mkdir(config.SPECIAL) except OSError: gau.emptyDir(config.SPECIAL) try: os.mkdir(config.INTER) except OSError: gau.emptyDir(config.INTER) gau.log_print( "[*] Checking executable base address" ) ############################################################################# # let us get the base address of the main executable. ifiles = os.listdir(config.INITIALD) for fl in ifiles: tfl = os.path.join(config.INITIALD, fl) try: f = open(tfl, 'r') f.close() except: gau.die("[-] Can not open our own input %s !" % (tfl)) (ibbs, iretc) = execute(tfl) if iretc != 128: # 0 gau.die("[-] Can't run the target program '%s' !" % (os.path.basename(config.SUT.replace(" ","").replace("%s","")))) break # we just want to run the executable once to get its load address imgOffFd = open("imageOffset.txt", 'r') for ln in imgOffFd: if "Main:" in ln: lst = ln.split() break config.LIBOFFSETS[0] = lst[1][:] imgOffFd.close() if config.LIBTOMONITOR != '' and config.LIBTOMONITOR != '#': gau.log_print("[+] Lib %s is at 0x%x" % (config.LIBTOMONITOR, int(config.LIBOFFSETS[1], 0))) gau.log_print( "[*] Checking pickles" ) ############################################################################# # open names pickle files gau.prepareBBOffsets() # lets initialize the BBFORPRUNE list from thie cALLBB set. if len(config.cALLBB) > 0: config.BBFORPRUNE = list(config.cALLBB) else: gau.log_print("[*] cALLBB is not initialized. something is wrong!!\n") system.exit() if config.PTMODE: pt = simplept.simplept() else: pt = None gau.log_print("[*] Running vuzzer for '%s'" % (os.path.basename(config.SUT.replace(" ","").replace("%s","")))) if config.ERRORBBON == True: gbb, bbb = dry_run() else: gbb = 0 # gau.die("dry run over..") import timing # selftest() noprogress = 0 currentfit = 0 lastfit = 0 config.CRASHIN.clear() stat = open("stats.log", 'w') stat.write("**** Fuzzing started at: %s ****\n" % (datetime.now().isoformat('+'),)) stat.write("**** Initial BB for seed inputs: %d ****\n" % (gbb,)) stat.flush() os.fsync(stat.fileno()) stat.write( "Genaration\t MINfit\t MAXfit\t AVGfit MINlen\t Maxlen\t AVGlen\t #BB\t AppCov\t AllCov\n") stat.flush() os.fsync(stat.fileno()) starttime = time.clock() allnodes = set() alledges = set() try: shutil.rmtree(config.INPUTD) except OSError: pass shutil.copytree(config.INITIALD, config.INPUTD) # fisrt we get taint of the intial inputs get_taint(config.INITIALD, 1) # print "MOst common offsets and values:", config.MOSTCOMMON # print "Base address: %s"%config.LIBOFFSETS[0] # raw_input("Press enter to continue..") config.MOSTCOMFLAG = True crashhappend = False filest = os.listdir(config.INPUTD) filenum = len(filest) if filenum < config.POPSIZE: gau.create_files(config.POPSIZE - filenum) gau.log_print( '[*] Population at start is about %d files' % (len(os.listdir(config.INPUTD)))) efd = open(config.ERRORS, "w") gau.prepareBBOffsets() writecache = True genran = 0 bbslide = 40 # this is used to call run_error_BB() functions keepslide = 3 keepfilenum = config.BESTP config.SEENBB.clear() # initialize set of BB seen so far, which is 0 del config.SPECIALENTRY[:] todelete = set() # temp set to keep file names that will be deleted in the special folder oldPrintSize = 0 while True: # print "[**] Generation %d\n***********"%(genran,) del config.TEMPTRACE[:] del config.BBSEENVECTOR[:] # this is set when a config.SPECIAL gets at least one new input per generation. SPECIALCHANGED = False config.TMPBBINFO.clear() config.TMPBBINFO.update(config.PREVBBINFO) fitnes = dict() execs = 0 config.cPERGENBB.clear() config.GOTSTUCK = False if config.ERRORBBON == True: if genran > config.GENNUM/5: bbslide = max(bbslide, config.GENNUM/20) keepslide = max(keepslide, config.GENNUM/100) keepfilenum = keepfilenum/2 if 0 < genran < config.GENNUM/5 and genran % keepslide == 0: copy_files(config.INPUTD, config.KEEPD, keepfilenum) # lets find out some of the error handling BBs if genran > 40 and genran % bbslide == 0: stat.write("\n**** Error BB cal started ****\n") stat.flush() os.fsync(stat.fileno()) run_error_bb(pt) copy_files(config.KEEPD, config.INPUTD, len(os.listdir(config.KEEPD))*1/10) # copy_files(config.INITIALD,config.INPUTD,1) files = os.listdir(config.INPUTD) per_gen_fnum = 0 for fl in files: per_gen_fnum += 1 tfl = os.path.join(config.INPUTD, fl) iln = os.path.getsize(tfl) args = (config.SUT % tfl).split(' ') progname = os.path.basename(args[0]) (bbs, retc) = execute(tfl) filecount = len(os.listdir(config.INPUTD)) inputname = os.path.basename(args[1]) per_current_stat = (float(per_gen_fnum)/float(filecount)) * 100 per_show = (int(float(filecount) / 10.0)) if per_show == 0: per_show = 10 if per_gen_fnum % per_show == 0 or per_gen_fnum == filecount: logStr = "[%s] Gen %d : %d%% Executed %d of %d" % (time.strftime("%H:%M:%S"),genran,int(per_current_stat),per_gen_fnum, filecount) if oldPrintSize > 0: dif = oldPrintSize - len(logStr) if dif > 0: logStr += " "*dif oldPrintSize = len(logStr) gau.log_print( logStr ) else: gau.log_print( ' '*oldPrintSize ) sys.stdout.write("\033[F") logStr = "[%s] Gen %d : %d%% Executed %d of %d [%s]" % (time.strftime("%H:%M:%S"),genran,int(per_current_stat),per_gen_fnum, filecount,inputname) if oldPrintSize > 0: dif = oldPrintSize - len(logStr) if dif > 0: logStr += " "*dif oldPrintSize = len(logStr) gau.log_print( logStr ) sys.stdout.write("\033[F") if config.BBWEIGHT == True: fitnes[fl] = gau.fitnesCal2(bbs, fl, iln) else: fitnes[fl] = gau.fitnesNoWeight(bbs, fl, iln) # raw_input() execs += 1 # let us prune the inputs(if at all), whose trace is subset of the new input just got executed. SPECIALADDED = False if config.GOTSPECIAL == True: SPECIALCHANGED = True SPECIALADDED = True todelete.clear() form_bitvector2(bbs, fl, config.BBFORPRUNE, config.SPECIALBITVECTORS) shutil.copy(tfl, config.SPECIAL) config.SPECIALENTRY.append(fl) for sfl, bitv in config.SPECIALBITVECTORS.iteritems(): if sfl == fl: continue if (config.SPECIALBITVECTORS[fl] & bitv) == bitv: tpath = os.path.join(config.SPECIAL, sfl) os.remove(tpath) todelete.add(sfl) config.SPECIALENTRY.remove(sfl) if sfl in config.TAINTMAP: del config.TAINTMAP[sfl] for ele in todelete: del config.SPECIALBITVECTORS[ele] if retc < 0 and retc != -2: efd.write("%s: %d\n" % (tfl, retc)) efd.flush() os.fsync(efd) tmpHash = sha1OfFile(config.CRASHFILE) if tmpHash not in crashHash: crashHash.append(tmpHash) tnow = datetime.now().isoformat().replace(":", "-") nf = "%s-%s.%s" % (progname, tnow, gau.splitFilename(fl)[1]) npath = os.path.join("outd/crashInputs", nf) shutil.copyfile(tfl, npath) if SPECIALADDED == False: shutil.copy(tfl, config.SPECIAL) config.CRASHIN.add(fl) if config.STOPONCRASH == True: # efd.close() crashhappend = True break fitscore = [v for k, v in fitnes.items()] maxfit = max(fitscore) avefit = sum(fitscore)/len(fitscore) mnlen, mxlen, avlen = gau.getFileMinMax(config.INPUTD) gau.log_print( "[*] Done with all input in Gen %d" % (genran) ) gau.log_print( "[*] Calculating code coverage" ) appcov, allcov = gau.calculateCov() tnow = datetime.now().isoformat().replace(":", "-") # stat.write("\t%d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %s\n"%(genran,min(fitscore),maxfit,avefit,mnlen,mxlen,avlen,len(config.cPERGENBB),appcov,allcov,tnow)) stat.write("\t%d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %d\t %s\n" % (genran, min( fitscore), maxfit, avefit, mnlen, mxlen, avlen, len(config.SEENBB), appcov, allcov, tnow)) stat.flush() os.fsync(stat.fileno()) gau.log_print( "[*] Wrote to stat.log" ) seenBB = len(config.SEENBB) codeCovStr = "[+] BB Code coverage is %d" % (seenBB) if config.LASTTURNCODECOV != 0: difLastTurnStr = "(no new BB found)" codeCovDif = seenBB - config.LASTTURNCODECOV if codeCovDif > 0: difLastTurnStr = "\033[0;32m(+%d BB)\033[0m" % (codeCovDif) elif codeCovDif < 0: difLastTurnStr = "\033[0;31m(-%d BB)\033[0m" % (codeCovDif) perBBTotal = (float(seenBB) / float(len(config.ALLBB))) * 100 difGoodBB = seenBB - len(config.GOODBB) difGoodBBStr = "no" if difGoodBB > 0: difGoodBBStr = "+%d" % (difGoodBB) elif difGoodBB < 0: difGoodBBStr = "-%d" % (difGoodBB) codeCovStr += " %s (%d%% of all BB) (%s BB dif with good path)" % (difLastTurnStr, perBBTotal, difGoodBBStr) config.LASTTURNCODECOV = seenBB gau.log_print( "-"*len(codeCovStr) ) gau.log_print( codeCovStr ) gau.log_print( "-"*len(codeCovStr) ) if crashhappend == True: break # lets find out some of the error handling BBs # if genran >20 and genran%5==0: # run_error_bb(pt) genran += 1 config.CURRENTGEN += 1 # this part is to get initial fitness that will be used to determine if fuzzer got stuck. lastfit = currentfit # currentfit=maxfit currentfit = len(config.SEENBB) # lastfit-config.FITMARGIN < currentfit < lastfit+config.FITMARGIN: if currentfit == lastfit: noprogress += 1 else: noprogress = 0 if noprogress > 20: config.GOTSTUCK = True stat.write("Heavy mutate happens now..\n") noprogress = 0 if (genran >= config.GENNUM) and (config.STOPOVERGENNUM == True): break if len(os.listdir(config.SPECIAL)) > 0 and SPECIALCHANGED == True: if len(os.listdir(config.SPECIAL)) < config.NEWTAINTFILES: get_taint(config.SPECIAL) else: try: os.mkdir(config.TAINTTMP) except OSError: gau.emptyDir(config.TAINTTMP) if conditional_copy_files(config.SPECIAL, config.TAINTTMP, config.NEWTAINTFILES) == 0: get_taint(config.TAINTTMP) # print "MOst common offsets and values:", config.MOSTCOMMON # gg=raw_input("press any key to continue..") gau.log_print( "[*] Going for new generation creation" ) gau.createNextGeneration3(fitnes, genran) # raw_input("press any key...") efd.close() stat.close() libfd_mm.close() libfd.close() endtime = time.clock() gau.log_print( "[**] Totol time %f sec" % (endtime-starttime,) ) gau.log_print( "[**] Fuzzing done. Check %s to see if there were crashes" % ( config.ERRORS,))
def create_child_input(self, parentInputFL, parentInput, conflicts, newMutation): ''' TODO ''' mutatedInput = parentInput[:] # craft the child input name # get the extension # TODO : find a better way to do it bn, ext = gautils.splitFilename(parentInputFL) name = "heavy-child-g%d-%d.%s" % (config.CURRENTGEN, self.childCount, ext) self.mutationHistory.update({name: dict()}) # remove old conflicts mutations # NOTE : if there is a index out of array here, there is a problem for c in conflicts: #for cmp in c.offsetsInInput: mutationHistoryData = self.mutationHistory[parentInputFL][ c.offsetsInInput[0]][1] for offset in mutationHistoryData: i = 0 for byte in mutationHistoryData[offset]: mutatedInput[offset + i] = (byte) i += 1 # register the new mutation in history self.mutationHistory[name].update( {newMutation.offsetsInInput[0]: [newMutation]}) # add the history directory self.mutationHistory[name][newMutation.offsetsInInput[0]].append( dict()) mutationHistoryData = self.mutationHistory[name][ newMutation.offsetsInInput[0]][1] # apply new mutation for offset in newMutation.offsetsInInput: mutationHistoryData.update({offset: []}) mutationHistoryDataCurrent = mutationHistoryData[offset] for i in range(0, newMutation.cmpSize): if int(offset + i) >= len(mutatedInput): break if newMutation.taintType == taintTypeEnum.UNKNOWN: # save the old value mutationHistoryDataCurrent.append(mutatedInput[(offset + i)]) # applying the conflicted parent mutation mutatedInput[(offset + i)] = self.currentMutation[i] elif newMutation.taintType == taintTypeEnum.SINGLE_BYTE: # save the old value mutationHistoryDataCurrent.append(mutatedInput[(offset + i)]) # applying the conflicted parent mutation mutatedInput[(offset + i)] = self.currentMutation[0] elif newMutation.taintType == taintTypeEnum.ARRAY: # save the old value mutationHistoryDataCurrent.append(mutatedInput[(offset + i)]) # applying the conflicted parent mutation # TODO : improve strategy mutatedInput[(offset + i)] = self.currentMutation[i] # apply magic bytes and most common bytes mutatedInput = gautils.apply_more_common_changes(mutatedInput) mutatedInput = gautils.apply_most_common_changes(mutatedInput) # build a string with the mutated input mutatedInput = ''.join(mutatedInput) return name, mutatedInput