def run(self): os.putenv("LANG", "C") os.putenv("ASAN_SYMBOLIZER_PATH", self.asan_symbolizer_path) cmd = self.program print "Running %s" % cmd cmd_obj = TimeoutCommand(cmd) cmd_obj.run(self.timeout, get_output=True) buf = cmd_obj.stderr self.asan.parse_buffer(buf) if self.asan.reason is not None: crash_data = CCrashData(self.asan.pc, self.asan.reason) i = 0 for line in self.asan.stack_trace: crash_data.add_data("stack trace", "%d" % i, (line[0], line[1])) i += 1 crash_data.add_data("registers", "pc", self.asan.pc) crash_data.add_data("registers", "bp", self.asan.bp) crash_data.add_data("registers", "sp", self.asan.sp) crash_data.add_data("disassembly", int(self.asan.pc), "") j = 0 for line in self.asan.additional: crash_data.add_data("information", j, line) j += 1 crash_data.disasm = [self.asan.pc, ""] if not self.asan.reason.startswith("SIG"): crash_data.exploitable = "EXPLOITABLE" else: crash_data.exploitable = "UNKNOWN" crash_data.add_data("exploitability", "reason", self.asan.reason) crash_data_buf = crash_data.dump_json() crash_data_dict = crash_data.dump_dict() line = "Program received %s at PC 0x%x SP 0x%x BP 0x%x" print line % (self.asan.reason, self.asan.pc, self.asan.sp, self.asan.bp) print for i, line in enumerate(self.asan.stack_trace): if i > 10: break print "0x%08x %s" % (line[0], line[1]) print print "Yep, we got a crash! \o/" print return crash_data_dict return
def run(self): global buf os.putenv("LANG", "C") logfile = mkstemp()[1] try: cmd = '/bin/bash -c "/usr/bin/gdb -q --batch --command=%s --args %s" 2>/dev/null > %s' cmd %= (self.gdb_commands, self.program, logfile) print cmd print "Running %s" % cmd cmd_obj = TimeoutCommand(cmd) #cmd_obj.shell = True cmd_obj.run(self.timeout) buf = open(logfile, "rb").readlines() self.parse_dump(buf) if self.signal: crash_data = CCrashData(self.pc, self.signal) i = 0 for stack in self.stack: crash_data.add_data("stack trace", "%d" % i, stack) i += 1 for reg in self.registers: crash_data.add_data("registers", reg, self.registers[reg]) crash_data.add_data("disassembly", int(self.pc), self.disasm) for dis in self.disasm_around: if type(dis[0]) is int or dis[0].isdigit(): crash_data.add_data("disassembly", dis[0], dis[1]) crash_data.disasm = [self.pc, self.disasm] if self.exploitability is not None: crash_data.exploitable = self.exploitability if self.exploitability_reason is not None: crash_data.add_data("exploitability", "reason", self.exploitability_reason) crash_data_buf = crash_data.dump_json() crash_data_dict = crash_data.dump_dict() print print "Yep, we got a crash! \o/" print return crash_data_dict return finally: os.remove(logfile)
def coverage(self, command, timeout=36000, hide_output=True): tool_path = self.path + "/source/tools/RunTracer" if int(self.arch) == 32: tool_path = tool_path + "/obj-ia32/ccovtrace.so" elif int(self.arch) == 64: tool_path = tool_path + "/obj-intel64/ccovtrace.so" logfile = mkstemp()[1] # XXX: Do we want to use the .sh script? Using this we're limiting # ourselves to only Linux and MacOSX. cmdline = "%s/pin.sh -t %s -o %s -- %s" if hide_output: # ...although, when using "hide_output", we're already doing it... cmdline += " >/dev/null 2>/dev/null" cmdline = cmdline % (self.path, tool_path, logfile, command) debug("Running command %s" % cmdline) cmd = TimeoutCommand(cmdline) ret = cmd.run(timeout) coverage = self.read_coverage_log(logfile) debug("Removing temporary file %s " % logfile) os.remove(logfile) debug("Returning coverage data...") cover = CCoverResults(coverage[0], coverage[1], ret) return cover
def coverage(self, command, timeout=36000, hide_output = True): tool_path = self.path+"/source/tools/RunTracer" if int(self.arch) == 32: tool_path = tool_path + "/obj-ia32/ccovtrace.so" elif int(self.arch) == 64: tool_path = tool_path + "/obj-intel64/ccovtrace.so" logfile = mkstemp()[1] # XXX: Do we want to use the .sh script? Using this we're limiting # ourselves to only Linux and MacOSX. cmdline = "%s/pin.sh -t %s -o %s -- %s" if hide_output: # ...although, when using "hide_output", we're already doing it... cmdline += " >/dev/null 2>/dev/null" cmdline = cmdline % (self.path, tool_path, logfile, command) debug("Running command %s" % cmdline) cmd = TimeoutCommand(cmdline) ret = cmd.run(timeout) coverage = self.read_coverage_log(logfile) debug("Removing temporary file %s " % logfile) os.remove(logfile) debug("Returning coverage data...") cover = CCoverResults(coverage[0], coverage[1], ret) return cover
def execute_command(self, cmd, timeout): ret = None if self.debugging_interface is None: cmd_obj = TimeoutCommand(cmd) ret = cmd_obj.run(timeout=self.timeout) if cmd_obj.stderr is not None: print cmd_obj.stderr else: self.iface.timeout = self.timeout if not has_pykd or self.iface != pykd_iface: if self.iface == asan_iface: crash = self.iface.main( asan_symbolizer_path=self.asan_symbolizer_path, args=cmd) else: crash = self.iface.main(cmd) else: # Avoid network timeouts and unnecessary delays when using pykd os.putenv("_NT_SYMBOL_PATH", "") crash = pykd_iface.main([cmd], timeout, mode=self.mode, windbg_path=self.windbg_path, exploitable_path=self.exploitable_path) if crash is not None: self.last_crash = crash ret = 0xC0000005 # Access violation in Windows return ret
def do_try(self, outdir, start_at=0): # Try to minimize to just one change current_change = 0 minimized = False iteration = 0 for i in range(len(self.diff)): for pos in self.diff: if start_at <= iteration: log("Minimizing, iteration %d (Max. %d)..." % (iteration, (len(self.diff)) * len(self.diff))) temp_file = tempfile.mktemp() buf = bytearray(self.template) if pos not in self.crash: continue buf[pos] = self.crash[pos] with open(temp_file, "wb") as f: f.write(buf) try: for key in self.env: os.putenv(key, self.env[key]) cmd = "%s %s" % (self.command, temp_file) cmd_obj = TimeoutCommand(cmd) ret = cmd_obj.run(timeout=self.timeout) if ret in RETURN_SIGNALS: log("Successfully minimized, caught signal %d (%s)!" % (ret, RETURN_SIGNALS[ret])) filename = sha1(buf).hexdigest() filename = os.path.join( outdir, "%s%s" % (filename, self.extension)) shutil.copy(temp_file, filename) log("Minized test case %s written to disk." % filename) minimized = True break finally: os.remove(temp_file) if minimized: break iteration += 1 if minimized: break value = self.diff.pop() if value in self.crash: self.template[value] = self.crash[value] del self.crash[value] if not minimized: log("Sorry, could not minimize crashing file!")
def do_try(self, outdir, start_at=0): # Try to minimize to just one change current_change = 0 minimized = False iteration = 0 for i in range(len(self.diff)): for pos in self.diff: if start_at <= iteration: log("Minimizing, iteration %d (Max. %d)..." % (iteration, (len(self.diff)) * len(self.diff))) temp_file = tempfile.mktemp() buf = bytearray(self.template) if pos not in self.crash: continue buf[pos] = self.crash[pos] with open(temp_file, "wb") as f: f.write(buf) try: for key in self.env: os.putenv(key, self.env[key]) cmd = "%s %s" % (self.command, temp_file) cmd_obj = TimeoutCommand(cmd) ret = cmd_obj.run(timeout=self.timeout) if ret in RETURN_SIGNALS: log("Successfully minimized, caught signal %d (%s)!" % (ret, RETURN_SIGNALS[ret])) filename = sha1(buf).hexdigest() filename = os.path.join(outdir, "%s%s" % (filename, self.extension)) shutil.copy(temp_file, filename) log("Minized test case %s written to disk." % filename) minimized = True break finally: os.remove(temp_file) if minimized: break iteration += 1 if minimized: break value = self.diff.pop() if value in self.crash: self.template[value] = self.crash[value] del self.crash[value] if not minimized: log("Sorry, could not minimize crashing file!")
def execute_command(self, cmd, timeout): ret = None if self.debugging_interface is None: cmd_obj = TimeoutCommand(cmd) ret = cmd_obj.run(timeout=self.timeout) else: self.iface.timeout = self.timeout if self.iface != pykd_iface: crash = self.iface.main(cmd) else: crash = pykd_iface.main([cmd], mode=self.mode, windbg_path=self.windbg_path, exploitable_path=self.exploitable_path) if crash is not None: ret = 0xC0000005 # Access violation in Windows return ret
def coverage(self, command, timeout=36000, hide_output = True): logdir = mkdtemp() cmdline = "%s/bin%s/drrun -t drcov -dump_text -logdir %s -- %s" if hide_output: cmdline += " >/dev/null 2>/dev/null" cmdline = cmdline % (self.path, self.arch, logdir, command) debug("Running command %s" % cmdline) cmd = TimeoutCommand(cmdline) ret = cmd.run(timeout) coverage = self.read_coverage_log(logdir) debug("Removing temporary directory %s " % logdir) shutil.rmtree(logdir) debug("Returning coverage data...") cover = CCoverResults(coverage[0], coverage[1], ret) return cover
def coverage(self, command, timeout=36000, hide_output=True): logdir = mkdtemp() cmdline = "%s/bin%s/drrun -t drcov -dump_text -logdir %s -- %s" if hide_output: cmdline += " >/dev/null 2>/dev/null" cmdline = cmdline % (self.path, self.arch, logdir, command) debug("Running command %s" % cmdline) cmd = TimeoutCommand(cmdline) ret = cmd.run(timeout) coverage = self.read_coverage_log(logdir) debug("Removing temporary directory %s " % logdir) shutil.rmtree(logdir) debug("Returning coverage data...") cover = CCoverResults(coverage[0], coverage[1], ret) return cover
def execute_command(self, cmd, timeout): ret = None if self.debugging_interface is None: cmd_obj = TimeoutCommand(cmd) ret = cmd_obj.run(timeout=self.timeout) if cmd_obj.stderr is not None: print cmd_obj.stderr else: self.iface.timeout = self.timeout if not has_pykd or self.iface != pykd_iface: crash = self.iface.main(cmd) else: os.putenv("_NT_SYMBOL_PATH", "") crash = pykd_iface.main([cmd], timeout, mode=self.mode, windbg_path=self.windbg_path, exploitable_path=self.exploitable_path) if crash is not None: ret = 0xC0000005 # Access violation in Windows return ret
def execute_command(self, cmd, timeout): ret = None if self.debugging_interface is None: cmd_obj = TimeoutCommand(cmd) ret = cmd_obj.run(timeout=self.timeout) if cmd_obj.stderr is not None: print cmd_obj.stderr else: self.iface.timeout = self.timeout if not has_pykd or self.iface != pykd_iface: if self.iface == asan_iface: crash = self.iface.main(asan_symbolizer_path=self.asan_symbolizer_path, args=cmd) else: crash = self.iface.main(cmd) else: # Avoid network timeouts and unnecessary delays when using pykd os.putenv("_NT_SYMBOL_PATH", "") crash = pykd_iface.main([cmd], timeout, mode=self.mode, windbg_path=self.windbg_path, exploitable_path=self.exploitable_path) if crash is not None: self.last_crash = crash ret = 0xC0000005 # Access violation in Windows return ret
def do_try(self, outdir, start_at=0): """ Try to remove a random number of lines iterating from the first line to the last one a number of times. Basically, we calculate a total number of lines to remove between 1 line and 10%. If the number of lines removed produces a test-case that still crashes, remove the lines from the template, otherwise, drop the changes and move to the next line. IDEAS: Remove all empty lines before starting? """ orig_lines = len(self.template) current_line = 0 iteration = 0 loops = 0 while 1: self.minimized = False total_lines = len(self.template) log("Starting loop %d" % loops) current_line = 0 for i in range(len(self.template)): self.read_configuration() log("Minimizing, iteration %d..." % iteration) iteration += 1 temp_file = tempfile.mktemp(suffix=self.extension) lines = self.template if current_line >= len(lines): break if loops == 0 and not self.line_per_line: # Rip a random number of lines between 1 and self.lines_percent # but only at the very first iteration (when we remove most of # the stuff). val = (total_lines-current_line)*self.lines_percent/100 if val == 0: val = 1 lines_to_rip = random.randint(1, val) log("Removing %d line(s) (maximum of %d%%)" % (lines_to_rip, self.lines_percent)) else: # For the likely final run remove only one line per try (or # whatever is specified in the configuration file) lines_to_rip = self.lines_to_rip log("Removing %d line(s)" % lines_to_rip) lines = lines[:current_line] + lines[current_line+lines_to_rip:] buf = "".join(lines) with open(temp_file, "wb") as f: f.write(buf) try: for key in self.env: os.putenv(key, self.env[key]) self.remove_crash_path() cmd = "%s %s" % (self.command, temp_file) cmd_obj = TimeoutCommand(cmd) ret = cmd_obj.run(timeout=self.timeout) if ret in RETURN_SIGNALS or (self.signal is not None and ret == self.signal) or \ self.crash_file_exists(): self.template = lines log("Process crashed as expected...") buf = "".join(self.template) if not os.path.exists(outdir): log("Directory %s does not exists, creating it..." % outdir) os.mkdir(outdir) filename = os.path.join(outdir, "last_minimized%s" % self.extension) with open(filename, "wb") as f: f.write(buf) log("Last minimized test case %s written to disk." % filename) else: current_line += 1 self.remove_crash_path() finally: os.remove(temp_file) loops += 1 if len(self.template) == total_lines: log("File minimized from %d line(s) to %d line(s)" % (orig_lines, len(self.template))) buf = "".join(self.template) filename = sha1(buf).hexdigest() filename = os.path.join(outdir, "%s%s" % (filename, self.extension)) with open(filename, "wb") as f: f.write(buf) log("Minimized test case %s written to disk." % filename) self.minimized = True break
def do_try(self, outdir, start_at=0): """ Try to remove a random number of lines iterating from the first line to the last one a number of times. Basically, we calculate a total number of lines to remove between 1 line and 10%. If the number of lines removed produces a test-case that still crashes, remove the lines from the template, otherwise, drop the changes and move to the next line. IDEAS: Remove all empty lines before starting? """ orig_lines = len(self.template) current_line = 0 iteration = 0 loops = 0 while 1: self.minimized = False total_lines = len(self.template) log("Starting loop %d" % loops) current_line = 0 for i in range(len(self.template)): self.read_configuration() log("Minimizing, iteration %d..." % iteration) iteration += 1 temp_file = tempfile.mktemp(suffix=self.extension) lines = self.template if current_line >= len(lines): break if loops == 0 and not self.line_per_line: # Rip a random number of lines between 1 and self.lines_percent # but only at the very first iteration (when we remove most of # the stuff). val = (total_lines - current_line) * self.lines_percent / 100 if val == 0: val = 1 lines_to_rip = random.randint(1, val) log("Removing %d line(s) (maximum of %d%%)" % (lines_to_rip, self.lines_percent)) else: # For the likely final run remove only one line per try (or # whatever is specified in the configuration file) lines_to_rip = self.lines_to_rip log("Removing %d line(s)" % lines_to_rip) lines = lines[:current_line] + lines[current_line + lines_to_rip:] buf = "".join(lines) with open(temp_file, "wb") as f: f.write(buf) try: for key in self.env: os.putenv(key, self.env[key]) self.remove_crash_path() cmd = "%s %s" % (self.command, temp_file) cmd_obj = TimeoutCommand(cmd) ret = cmd_obj.run(timeout=self.timeout) if ret in RETURN_SIGNALS or (self.signal is not None and ret == self.signal) or \ self.crash_file_exists(): self.template = lines log("Process crashed as expected...") buf = "".join(self.template) if not os.path.exists(outdir): log("Directory %s does not exists, creating it..." % outdir) os.mkdir(outdir) filename = os.path.join( outdir, "last_minimized%s" % self.extension) with open(filename, "wb") as f: f.write(buf) log("Last minimized test case %s written to disk." % filename) else: current_line += 1 self.remove_crash_path() finally: os.remove(temp_file) loops += 1 if len(self.template) == total_lines: log("File minimized from %d line(s) to %d line(s)" % (orig_lines, len(self.template))) buf = "".join(self.template) filename = sha1(buf).hexdigest() filename = os.path.join(outdir, "%s%s" % (filename, self.extension)) with open(filename, "wb") as f: f.write(buf) log("Minimized test case %s written to disk." % filename) self.minimized = True break
def run_test(self, name, data): track_lines = int(data[0]) track_return = int(data[1]) mitigate_lines = int(data[2]) mitigate_return = int(data[3]) args = ["-track 1", "-track 1 -mitigate 1"] archs = ["ia32", "intel64"] for arch in archs: failed = False tmp_cmd = "%s/pin -t %s/obj-%s/%s" % ( self.pin_path, self.tool_path, arch, self.tool_name) for arg in args: suffix = "" if arch == "ia32": suffix = "32" cmd = "%s %s -- %s/%s%s" % ( tmp_cmd, arg, self.testcases_directory, name, suffix) debug("Running %s" % cmd) t = TimeoutCommand(cmd) code = t.run(get_output=True) stdout = t.stdout if arg.find("mitigate") == -1: if code != track_return: failed = True line = "*** TEST %s FAILED *** Different return code for tracker: got %d, expected %d" log(line % (repr(name), code, track_return)) print "-" * 80 print repr(stdout) print "-" * 80 lines = stdout.count("\n") if lines != track_lines: failed = True line = "*** TEST %s FAILED *** Different number of lines for tracker: got %d, expected %d" log(line % (repr(name), lines, track_lines)) print "-" * 80 print repr(stdout) print "-" * 80 else: if code != mitigate_return: failed = True line = "*** TEST %s FAILED *** Different return code for mitigator: got %d, expected %d" log(line % (repr(name), code, mitigate_return)) print "-" * 80 print repr(stdout) print "-" * 80 lines = stdout.count("\n") if lines != mitigate_lines: failed = True line = "*** TEST %s FAILED *** Different number of lines for mitigator: got %d, expected %d" log(line % (repr(name), lines, mitigate_lines)) print "-" * 80 print repr(stdout) print "-" * 80 if not failed: test_type = "tracker" if arg.find("mitigate") == -1: test_type = "mitigator" log("TEST %s FOR %s ARCH %s PASSED" % (repr(name), test_type, arch)) else: self.failed = True