def extract(self, binary, logfile): protos = dict() with open(binary, 'rb') as f: e = ELFFile(f) symtable = e.get_section_by_name(".symtab") if not symtable: Log.stderr("symbole table not found") return protos for entry in symtable.iter_symbols(): if "FUNC" in entry['st_info'].type: name = entry.name addr = entry["st_value"] ret = entry["st_value"] protos[addr] = (name, addr, ret) dynsym = e.get_section_by_name(".dynsym") reloc = e.get_section_by_name(".rela.plt") plt_base = e.get_section_by_name(".plt")['sh_addr'] # Additionally add plt entries for idx, entry in enumerate(reloc.iter_relocations()): name = dynsym.get_symbol(entry['r_info_sym']).name + "@plt" addr = plt_base + 16 * (idx + 1) protos[addr] = (name, addr, addr) with open(logfile, "w") as f: for (name, entry, ret) in protos.values(): f.write("{}:{}:{}\n".format(name, entry, ret))
def launch_analysis(args, analysis=None): if analysis is None: analysis = args.analysis # Read configuration file tpl = Confiture("config/template.yaml") config = tpl.check_and_get("config/config.yaml") # Check if temporary repo exists if not os.path.exists("tmp"): os.makedirs("tmp") # Extract dwarf info SymExtractor().extract(args.pgm, "tmp/dwarf.txt") # Create Pintool object pintool = Pintool( config["pin"]["path"], config["pin"]["bin"], config["pin"]["compile-flags"], config["pintool"]["src"], config["pintool"]["obj"], ) # Log file if args.log: logidir = args.log else: logdir = default_log_dir(args.pgm) # Compile pintool Log.stdout("compiling pintool") if not pintool.compile(analysis if analysis else args.analysis): Log.stdout("compilation exited with non-zero status") exit() if "oracle" in analysis: infile = "tmp/dwarf.txt" else: infile = None # Launch pintool Log.stdout("launching analysis on {}".format(args.pgm)) pintool.launch(args.pgm, args.args, infile, logdir, "-l true" if not args.ignore_libs else None) # Get results for an in analysis: Log.stdout("extracting results") res = Result(default_log_path(logdir, args.pgm, an)) # Print the results Log.stdout("results of inference:") res.display()
def __init__(self, path, name=""): self.__fn = dict() self.__name = name last_fn = None with open(path, "r") as f: # First line is elapsed time self.__time = f.readline()[:-1] for line in f.readlines(): typ = line.split(":")[0] if typ == 'F': fn = Function(*line[:-1].split(":")[1:]) self.__fn.setdefault(fn.iname, list()) self.__fn[fn.iname].append(fn) last_fn = fn elif typ == 'I': ins = Instruction(line[:-1].split(":")[1:]) if last_fn is not None: last_fn.add_ins(ins) else: Log.stderr("unrecognized format: {}".format(line))
def pintool_analysis(analysis, args): # Read configuration file tpl = Confiture("config/template.yaml") config = tpl.check_and_get("config/config.yaml") # Check if temporary repo exists if not os.path.exists("tmp"): os.makedirs("tmp") # Create Pintool object call = Pintool( config["pin"]["path"], config["pin"]["bin"], config["pin"]["compile-flags"], config["analysis"][analysis]["src"], config["analysis"][analysis]["obj"], ) # Log file if args.log: log = args.log else: log = default_log_path(args.pgm, analysis) # Compile pintool Log.stdout("compiling pintool") if not call.compile(): Log.stdout("compilation exited with non-zero status") exit() # Launch pintool Log.stdout("launching analysis on {}".format(args.pgm)) call.launch(args.pgm, args.args, None, log) # Get results Log.stdout("extracting results") res = Result(log, analysis) # Print the results Log.stdout("results of inference:") res.display()
def print_ins(self, ref, prefix="", ignore_ld=False): uniq = 0 for ins in self.__ins: if "ld-linux" in ins.img and ignore_ld: continue # Look for the reference instruction (if exists) refi = None if ref: for r in ref: if r.addr == ins.addr: refi = r break if refi: nb_catch = refi.nb_hit else: nb_catch = 0 if nb_catch != ins.nb_hit: Log.stdout("{}{} [seen {}]".format(prefix, ins, nb_catch), prefix="", lvl=3) uniq += 1 return uniq
def diff_analysis(args): # if args.file1: # if not args.file2: # Log.stderr("you must specify two files") # exit() # oracle = Result(args.file1) # other = Result(args.file2) if not args.analysis or len(args.analysis) < 2: Log.stderr("you must specify at least two analysis") exit() if not args.pgm: Log.stderr("you must specify a program to analyze") exit() if args.run: lvl = Log.get_verbose() Log.set_verbose(0) launch_analysis(args) Log.set_verbose(lvl) logdir = default_log_dir(args.pgm) path = default_log_path(logdir, args.pgm, args.analysis[0]) oracle = Result(path, args.analysis[0]) for an in args.analysis[1:]: path = default_log_path(logdir, args.pgm, an) other = Result(path, an) if Log.get_verbose() < 3: Log.stdout("pgm={}".format(os.path.basename(args.pgm)), lvl=1, prefix="") Log.stdout("args={}".format(args.args), lvl=1, prefix="") Log.stdout("analysis1={}".format(oracle.name), lvl=1, prefix="") Log.stdout("time1={}".format(oracle.time), lvl=1, prefix="") Log.stdout("analysis2={}".format(other.name), lvl=1, prefix="") Log.stdout("time2={}".format(other.time), lvl=1, prefix="") other.diff(oracle, ignore_ld=args.ignore_ld)
def diff(self, oracle, ignore_ld=False, no_ins=False): # Total number of errors misses = 0 extra = 0 # number of unique instructions causing errors u_misses, u_extra = 0, 0 tot = 0 for img, fn in oracle.fn.items(): if "ld-linux" in img and ignore_ld: continue self.__fn.setdefault(img, list()) Log.stdout(" | {}".format(img), prefix="", lvl=3) Log.stdout(" |", prefix="", lvl=3) for f in fn: g = None for gg in self.__fn[img]: if gg.offset == f.offset: g = gg break if g is None: ncalls = 0 else: ncalls = g.calls if ncalls < f.calls: misses += (f.calls - ncalls) Log.stdout(" | (@{:012x}) {} - {} calls missed".format( f.offset, f.name, f.calls - ncalls), prefix="", lvl=3) u_misses += f.print_ins(g.ins if g else None, " | ", ignore_ld) elif f.calls < ncalls: extra += (ncalls - f.calls) Log.stdout(" | (@{:012x}) {} - {} extra calls".format( f.offset, f.name, -f.calls + ncalls), prefix="", lvl=3) u_extra += f.print_ins(g.ins if g else None, " | ", ignore_ld) tot += f.calls for g in self.__fn[img]: f = None for ff in fn: if ff.offset == g.offset: f = ff break if f is None: extra += g.calls Log.stdout(" | (@{:012x}) {} - {} extra calls".format( g.offset, g.name, g.calls), prefix="", lvl=3) u_extra += g.print_ins(None, " | ", ignore_ld) Log.stdout(" |", prefix="", lvl=3) Log.stdout(" | TOTAL CALLS (ORACLE): {}".format(tot), prefix="", lvl=3) Log.stdout(" | TOTAL MISSES: {} ({} unique instructions)".format( misses, u_misses), prefix="", lvl=3) Log.stdout(" | TOTAL EXTRA: {} ({} unique instructions)".format( extra, u_extra), prefix="", lvl=3) if Log.get_verbose() < 3: Log.stdout("calls={}".format(tot), prefix="", lvl=1) Log.stdout("misses={}".format(misses), prefix="", lvl=1) Log.stdout("u_misses={}".format(u_misses), prefix="", lvl=1) Log.stdout("extra={}".format(extra), prefix="", lvl=1) Log.stdout("u_extra={}".format(u_extra), prefix="", lvl=1)
def display(self): tot_calls = 0 for img, fn in self.__fn.items(): Log.stdout(" | # {}".format(img), lvl=3, prefix="") for f in sorted(fn, key=lambda a: -a.calls): if f.calls == 0: break Log.stdout(" | \t{}".format(f), lvl=3, prefix="") tot_calls += f.calls Log.stdout(" | ", prefix="", lvl=3) Log.stdout(" | TOTAL: {} calls".format(tot_calls), lvl=3, prefix="") # If verbose was lower than the lvl of previous messages if Log.get_verbose() < 3: # Log time Log.stdout("time={}".format(self.__time), lvl=1, prefix="") # Log summary Log.stdout("calls={}".format(tot_calls), lvl=1, prefix="")
parser.add_argument('-v', action="store_const", const=True, help="output detailed results") parser.add_argument('-q', action="store_const", const=True, help="quiet mode -- only output the minimum result info") parser.add_argument('--run', action="store_const", const=True, metavar="RUN", help="Re-run instrumentations before diff") args = parser.parse_args() if args.v: Log.set_verbose(3) if args.q: Log.set_verbose(1) if args.res: Log.set_output(args.res) if args.cmd[0] == "launch": launch_analysis(args) elif args.cmd[0] == "diff": diff_analysis(args) else: Log.stderr("unknown analysis: {}".format(args.analysis[0])) Log.close()