# coding=utf-8 import time from PyQt5 import QtCore, QtWidgets from path import Path import idc import idaapi import idautils from idasec.network.commands import * from idasec.proto.analysis_config_pb2 import specific_parameters_t from idasec.proto.analysis_config_pb2 import po_analysis_results from idasec.analysis.default_analysis import DefaultAnalysis, STATIC_AND_DYNAMIC from idasec.widgets.StandardResultWidget import StandardResultWidget from idasec.report_generator import make_cell, RED, GREEN, PURPLE, ORANGE, BLACK, HTMLReport import idasec.utils as utils from idasec.ida_utils import MyFlowGraph, get_succs, Status from idasec.trace import make_header, chunk_from_path from idasec.formula import * from idasec.ui.static_iteration_config_ui import Ui_static_iteration_config # =============================== CONFIGURATION ============================= # =========================================================================== class StaticOpaqueConfigWidget(QtWidgets.QWidget, Ui_static_iteration_config): def __init__(self): super(StaticOpaqueConfigWidget, self).__init__() self.setupUi(self) self.horizontalLayout_2.setEnabled(False) self.radio_addr.toggled.connect(self.addr_radio_toggled) self.radio_routine.toggled.connect(self.routine_radio_toggled) self.radio_program.toggled.connect(self.program_radio_toggled) self.target_button.clicked.connect(self.target_button_clicked) self.radio_addr.setChecked(True) self.radio_path_routine.setChecked(True) def set_fields(self, json_fields): # Set the target field if a target is given in standard_params try: target = json_fields["standard_params"]["target_addr"] self.targetlineedit.setText(hex(target)) self.radio_addr.setChecked(True) except KeyError: pass def serialize(self): s = str(self.target_field.text()) if self.radio_addr.isChecked(): try: int(s, 16) except ValueError: print "Bad address given" return None elif self.radio_routine.isChecked(): addr = idc.LocByName(s) if addr == idc.BADADDR: print "Bad function name given" return None return specific_parameters_t() def addr_radio_toggled(self, enabled): if enabled: self.target_label.setText("Addr:") def routine_radio_toggled(self, enabled): if enabled: self.target_label.setText("Name:") def program_radio_toggled(self, enabled): self.target_label.setVisible(not enabled) self.target_field.setVisible(not enabled) self.target_button.setVisible(not enabled) def target_button_clicked(self): if self.radio_addr.isChecked(): self.target_field.setText(hex(idc.here())) else: self.target_field.setText(idc.GetFunctionName(idc.here())) # ================================================================================ # ================================================================================ # ==================== Data structures ================== AddrRet = namedtuple("AddrRet", "status k dependency predicate distance alive_branch dead_branch") cond_jump = ["jz", "jnz", "ja", "jae", "jnb", "jnc", "jb", "jc", "jbe", "jna", "je", "jne", "jg", "jge", "jnl", "jl", "jle", "jng"] def to_status_name(x): return {po_analysis_results.UNKNOWN: "Unknown", po_analysis_results.NOT_OPAQUE: "Covered", po_analysis_results.OPAQUE: "Opaque", po_analysis_results.LIKELY: "Likely"}[x] def status_to_color(x): return {po_analysis_results.UNKNOWN: PURPLE, po_analysis_results.LIKELY: ORANGE, po_analysis_results.NOT_OPAQUE: GREEN, po_analysis_results.OPAQUE: RED}[x] # ======================================================= # ===================================== ANALYSIS ======================================= # ====================================================================================== # ANNOT_CODE = "Annotate opaque jumps" # GENERATE_PLOT = "Generate plot chart" HIGHLIGHT_DEAD_CODE = "Highlight dead code" HIGHLIGHT_SPURIOUS_CALCULUS = "Highlight spurious computation" EXPORT_RESULT = "Export results" EXTRACT_REDUCED_CFG = "Extract reduced CFG" class StaticOpaqueAnalysis(DefaultAnalysis): config_widget = StaticOpaqueConfigWidget() name = "static opaque" kind = STATIC_AND_DYNAMIC @staticmethod def on_analysis_selected(widget): print "Analyse selection changed !" index = widget.default_action_selector.findText("SYMB") widget.default_action_selector.setCurrentIndex(index) widget.direction_selector_changed("Backward") index = widget.policy_selector.findText("SS") widget.policy_selector.setCurrentIndex(index) # TODO Save somewhere the what was selected, and restore it def __init__(self, parent, config, is_stream=False, trace=None): DefaultAnalysis.__init__(self, parent, config, is_stream, trace) self.actions = {# ANNOT_CODE: (self.annotate_code, False), # GENERATE_PLOT: (self.generate_chart, False), HIGHLIGHT_DEAD_CODE: (self.highlight_dead_code, False), HIGHLIGHT_SPURIOUS_CALCULUS: (self.highlight_spurious, False), EXPORT_RESULT: (self.export_result, False), EXTRACT_REDUCED_CFG: (self.extract_reduced_cfg, False)} self.results = {} self.result_widget = StandardResultWidget(self) self.result_widget.verticalLayout.addLayout(self.make_progress_bar(self.result_widget)) self.STOP = False self.functions_cfg = {} self.functions_candidates = {} self.functions_spurious_instrs = {} self.po = po_analysis_results() self.k = -1 self.report = HTMLReport() self.report.add_title("Opaque predicates Detection", size=2) self.exec_time_dep = 0 self.exec_time_total = 0 def run(self): # -- GUI stuff self.result_widget.set_actions_visible_and_enabled(False) self.set_progress_visible(True) # ----------- # Refill the configuration file if self.configuration.ksteps != 0 and self.config_widget.radio_path_routine.isChecked(): self.k = self.configuration.ksteps # Use the ksteps given if making the path on the whole routine self.result_widget.webview.append("### Opaque predicates Detection ###\n") self.configuration.analysis_name = "static opaque" self.configuration.additional_parameters.typeid = self.configuration.additional_parameters.STANDARD target_val = str(self.config_widget.target_field.text()) start_tps = time.time() if self.config_widget.radio_addr.isChecked(): addr = utils.to_addr(target_val) self.process_routine(idaapi.get_func(addr).startEA, pred_addr=addr) elif self.config_widget.radio_routine.isChecked(): addr = idc.LocByName(target_val) if addr == idc.BADADDR: addr = utils.to_addr(target_val) self.process_routine(addr) elif self.config_widget.radio_program.isChecked(): self.process_program() else: pass self.exec_time_total = time.time() - start_tps - self.exec_time_dep self.analyse_finished = True self.broker.terminate() # -- GUI stuff self.result_widget.set_actions_visible_and_enabled(True) self.set_progress_visible(False) # ------------ self.analysis_terminated() def process_program(self): funs = list(idautils.Functions()) nb = len(funs) for i, fun in zip(xrange(nb), funs): self.process_routine(fun, rtn_i=i+1, total_rtn=nb) if self.STOP: return def process_routine(self, rtn_addr, pred_addr=None, rtn_i=1, total_rtn=1): if rtn_addr not in self.functions_cfg: self.functions_cfg[rtn_addr] = MyFlowGraph(rtn_addr) cfg = self.functions_cfg[rtn_addr] path_to = self.config_to_path_function(cfg) if pred_addr is None: candidates = {x for x in idautils.FuncItems(rtn_addr) if idc.GetMnem(x) in cond_jump} else: candidates = {pred_addr} nb_candidates = len(candidates) self.functions_candidates[rtn_addr] = set() self.functions_spurious_instrs[rtn_addr] = set() self.progressbar_loading.reset() self.progressbar_loading.setMaximum(len(candidates)) name = idc.GetFunctionName(rtn_addr) self.result_widget.webview.append("\n=> Function:%s\n" % name) self.log("[result]", "Start processing function: 0x%x" % rtn_addr) for i, addr in zip(xrange(len(candidates)), candidates): path = path_to(addr) res = self.process_addr(rtn_addr, addr, path) if self.STOP: return elif res is None: continue dead_br = "/" if res.dead_branch is None else "%x" % res.dead_branch self.result_widget.webview.append("%x:\t%s\t\tK:%d\tDead:%s" % (addr, to_status_name(res.status), res.k, dead_br)) self.result_widget.webview.verticalScrollBar().setValue(self.result_widget.webview.verticalScrollBar().maximum()) self.loading_stat.setText("Fun: %d/%d Addr: %d/%d" % (rtn_i, total_rtn, i+1, nb_candidates)) self.progressbar_loading.setValue(self.progressbar_loading.value()+1) self.functions_candidates[rtn_addr].add(addr) def process_addr(self, rtn_addr, addr, path, rec=5): succs = get_succs(addr) if len(succs) > 2: print "Addr: %x have more than two sucessors" % addr return k = len(path) if self.k == -1 else self.k self.configuration.ksteps = k self.configuration.additional_parameters.standard_params.target_addr = addr self.configuration.additional_parameters.standard_params.get_formula = True for res in self.send_query_binsec(self.configuration, path[-k:], addr): self.po.ParseFromString(res) if len(self.po.values) != 1: print "Wrong number of results for %x: %d" % (addr, len(self.po.values)) else: po = self.po.values[0] before = time.time() formula_dep, predicate, distance = self.compute_dependency_and_predicate(po.formula) if po.formula else ([], "", 0) # formula_dep, predicate, distance = ([], "", 0) self.exec_time_dep += time.time() - before dead_branch = [x for x in succs if x != po.alive_branch][0] if po.status == self.po.OPAQUE else None if po.status == self.po.OPAQUE: self.functions_spurious_instrs[rtn_addr].update(formula_dep+[addr]) self.results[addr] = AddrRet(po.status, k, formula_dep, predicate, distance, po.alive_branch, dead_branch) # print "End processing address:%x status:%s" % (addr, to_status_name(ret.status)) if addr in self.results: return self.results[addr] else: if self.STOP or rec == 0: # The timeout was probably triggered print("Ignore %x" % addr) return None else: print("Restart: %x" % addr) self.process_addr(rtn_addr, addr, path, rec=rec-1) def send_query_binsec(self, conf, path, addr): self.broker.send_binsec_message(START_ANALYSIS, conf.SerializeToString()) header = make_header().SerializeToString() chunk = chunk_from_path(path).SerializeToString() # self.dump_trace(header, chunk, addr) self.broker.send_binsec_message(TRACE_HEADER, header) self.broker.send_binsec_message(TRACE_CHUNK, chunk) self.broker.send_binsec_message(END, EMPTY) before = time.time()+10 for origin, cmd, data in self.broker.run_broker_loop_generator(): QtWidgets.QApplication.processEvents() if self.STOP: break if origin == BINSEC: if cmd == "END": break elif cmd == ANALYSIS_RESULTS: yield data else: if data.find("Not decoded") == data.find("Undecoded instr") == -1: self.log(cmd, data) elif origin is None and time.time() > before: print "Timeout over !" break @staticmethod def dump_trace(header, chunk, addr): import struct f = open("/tmp/%x.dmp" % addr, "wb") f.write(struct.pack("I", len(header))) f.write(header) f.write(struct.pack("I", len(chunk))) f.write(chunk) f.close() def compute_dependency_and_predicate(self, raw_formula): f = SMTFormula() f.parse(raw_formula) offsets, bin_op, distance = self.slice(f) pred = self.expr_synthesis(bin_op) if bin_op else u"" # print "compute_dependency: ", pred, offsets #,"\n",bin_op return offsets, pred, distance def config_to_path_function(self, cfg): if self.config_widget.radio_path_routine.isChecked(): return cfg.full_path_to elif self.config_widget.radio_path_basicblock.isChecked(): return cfg.bb_path_to elif self.config_widget.radio_path_safe.isChecked(): return cfg.safe_path_to else: assert False def analysis_terminated(self): self.log("[info]", "Analysis %s terminated" % self.name) self.refine_results() # self.compute_dead_code() self.propagate_liveness() self.generate_opaqueness_stats_report() self.generate_dead_code_stats_report() self.generate_opaqueness_details_report() # TODO: Stat of pattern used etc.. self.result_widget.webview.setHtml(self.report.generate()) def refine_results(self): likely_retag = 0 fp_retag = 0 fn_retag = 0 for rtn_addr, candidates in self.functions_candidates.items(): for addr in sorted(candidates): res = self.results[addr] val = sum([x in res.predicate for x in ["(0 :: 2)", "7x", "7y", u"²"]]) final_status = res.status alive, dead = res.alive_branch, res.dead_branch if res.status == self.po.NOT_OPAQUE: if val != 0: fn_retag += 1 final_status = self.po.OPAQUE jmp_target = [x for x in idautils.CodeRefsFrom(addr, 0)][0] next_target = [x for x in idautils.CodeRefsFrom(addr, 1) if x != jmp_target][0] alive, dead = (next_target, jmp_target) if idc.GetDisasm(addr)[:2] == "jz" else (jmp_target, next_target) self.functions_spurious_instrs[rtn_addr].update(res.dependency+[addr]) elif res.status == self.po.OPAQUE: if val == 0: fp_retag += 1 final_status = self.po.NOT_OPAQUE elif res.status == self.po.LIKELY: if val == 0: final_status = self.po.NOT_OPAQUE else: final_status = self.po.OPAQUE jmp_target = [x for x in idautils.CodeRefsFrom(addr, 0)][0] next_target = [x for x in idautils.CodeRefsFrom(addr, 1) if x != jmp_target][0] alive, dead = (next_target, jmp_target) if idc.GetDisasm(addr)[:2] == "jz" else (jmp_target, next_target) self.functions_spurious_instrs[rtn_addr].update(res.dependency+[addr]) likely_retag += 1 self.results[addr] = AddrRet(final_status, res.k, res.dependency, res.predicate, res.distance, alive, dead) print "Retag: FP->OK:%d" % fp_retag print "Retag: FN->OP:%d" % fn_retag print "Retag: Lkl->OK:%d" % likely_retag def generate_opaqueness_stats_report(self): self.report.add_title('Stats opaqueness', size=3) self.report.add_table_header(["type", "number", "percentage"]) total = len(self.results) for state in [self.po.OPAQUE, self.po.NOT_OPAQUE, self.po.UNKNOWN]: nb = len([x for x in self.results.values() if x.status == state]) avg = float(nb*100)/(total if total != 0 else 1) state_cell = make_cell(to_status_name(state), bold=True, color=status_to_color(state)) self.report.add_table_line([state_cell, make_cell(str(nb)), make_cell("%d%c" % (avg, '%'))]) self.report.end_table() def generate_dead_code_stats_report(self): stats = {"DEAD": 0, "ALIVE": 0, "SPURIOUS": 0, "UNKNOWN": 0} to_color = {"DEAD": RED, "ALIVE": GREEN, "SPURIOUS": ORANGE, "UNKNOWN": BLACK} for cfg in self.functions_cfg.values(): for bb in cfg.values(): if bb.is_dead(): stats["DEAD"] += bb.size() elif bb.is_unknown(): stats["UNKNOWN"] += bb.size() elif bb.is_alive(): for i, st in bb.instrs_status.items(): if st == Status.ALIVE: stats["ALIVE"] += 1 elif st == Status.DEAD: stats["SPURIOUS"] += 1 else: stats["UNKNOWN"] += 1 self.report.add_title('Stats Dead Code', size=3) self.report.add_table_header(["type", "number", "percentage"]) total = sum(stats.values()) for st, nb in stats.items(): avg = float(nb*100)/(total if total != 0 else 1) self.report.add_table_line([make_cell(st, color=to_color[st]), make_cell(str(nb)), make_cell("%d%c" % (avg, '%'))]) self.report.end_table() def generate_opaqueness_details_report(self): for rtn_addr, candidates in self.functions_candidates.items(): self.report.add_title('%s' % idc.GetFunctionName(rtn_addr), size=3) self.report.add_table_header(['address', "status", "K", "predicate", "distance", "dead branch"]) for addr in sorted(candidates): res = self.results[addr] status, color = to_status_name(res.status), status_to_color(res.status) status = make_cell(status, bold=True, color=color) dead_br_cell = make_cell("/" if res.dead_branch is None else "%x" % res.dead_branch) self.report.add_table_line([make_cell("%x" % addr), status, make_cell(str(res.k)), make_cell(res.predicate), make_cell(str(res.distance)), dead_br_cell]) self.report.end_table() def propagate_liveness(self): for fun_addr, cfg in self.functions_cfg.items(): candidates = self.functions_candidates[fun_addr] spurious = self.functions_spurious_instrs[fun_addr] worklist = [cfg[0]] while worklist: bb = worklist.pop() if bb.status == Status.UNKNOWN: # print "%d 0x%x marked ALIVE" % (bb.id, bb.startEA) bb.status = Status.ALIVE last = bb.last() succs = list(bb.succs()) if last in candidates: infos = self.results[last] if infos.status == self.po.OPAQUE: succ = [x for x in succs if x.startEA == infos.alive_branch][0] # print "Append %x to worklist" % succ.startEA worklist.append(succ) else: # print "Append all child to worklist" worklist.extend(succs) else: # Just propagate normally # print "last 0x%x not in candidates" % last worklist.extend(succs) for i in bb: # Update the status for instructions st = Status.DEAD if i in spurious else Status.ALIVE bb.set_instr_status(i, st) for bb in [x for x in cfg.values() if x.status == Status.UNKNOWN]: bb.status = Status.DEAD # -- Action handlers def annotate_code(self, _): print "Annotate code !" def generate_chart(self, _): print "Generate chart !" def highlight_dead_code(self, enabled): curr_fun = idaapi.get_func(idc.here()).startEA cfg = self.functions_cfg[curr_fun] # for cfg in self.functions_cfg.values(): for bb in cfg.values(): color = {Status.DEAD: 0x5754ff, Status.ALIVE: 0x98FF98, Status.UNKNOWN: 0xaa0071}[bb.status] color = 0xFFFFFF if enabled else color for i in bb: idc.SetColor(i, idc.CIC_ITEM, color) self.actions[HIGHLIGHT_DEAD_CODE] = (self.highlight_dead_code, not enabled) self.result_widget.action_selector_changed(HIGHLIGHT_DEAD_CODE) def highlight_spurious(self, enabled): print "Highlight spurious clicked !" curr_fun = idaapi.get_func(idc.here()).startEA cfg = self.functions_cfg[curr_fun] color = 0xFFFFFF if enabled else 0x507cff for bb in [x for x in cfg.values() if x.is_alive()]: # Iterate only alive basic blocks for i, st in bb.instrs_status.items(): if st == Status.DEAD: # Instructions dead in alive basic blocks are spurious idc.SetColor(i, idc.CIC_ITEM, color) self.actions[HIGHLIGHT_SPURIOUS_CALCULUS] = (self.highlight_spurious, not enabled) self.result_widget.action_selector_changed(HIGHLIGHT_SPURIOUS_CALCULUS) def export_result(self, _): filename = QtWidgets.QFileDialog.getSaveFileName()[0] filepath = Path(filename) if not filepath.exists() and filepath != '': report = filepath if filepath.ext == ".html" else filepath.dirname() / filepath.namebase+".html" raw = filepath.dirname() / filepath.namebase+".csv" html_file = filepath.dirname() / filepath.namebase+".html" html_file.write_bytes(self.report.generate()) report.write_text(self.report.generate()) f = raw.open("w") for addr, infos in self.results.iteritems(): f.write_bytes(u"0x%x,%s,%d,%s,0x%x,0x%x\n" % (addr, to_status_name(infos.status), infos.k, infos.dependency, infos.alive_branch, infos.dead_branch)) f.close() self.log("[info]", "Export done in %s and %s" % (report.basename(), raw.basename())) else: self.log("[error]", "File already exists.. (do not save)") def extract_reduced_cfg(self, _): # TODO: Make a copy of the CFG before stripping it print "Extract reduced CFG" curr_fun = idaapi.get_func(idc.here()).startEA cfg = self.functions_cfg[curr_fun] po_addrs = {k for k, v in self.results.items() if v.status == self.po.OPAQUE} cfg.remove_dead_bb() # Dead basic block removal step # Relocation + Merge step for idx, bb in cfg.items(): print "try reduce: %d: 0x%x" % (idx, bb.startEA) if bb.is_full_spurious() and bb.nb_preds() == bb.nb_succs() == 1: # Do relocation bb_pred = list(bb.preds())[0] bb_succ = list(bb.succs())[0] print " relocation bind %d->%d" % (bb_pred.id, bb_succ.id) bb_pred.remove_succ(bb) bb_pred.add_succ(bb_succ) bb_succ.remove_pred(bb) bb_succ.add_pred(bb_pred) cfg.pop(idx) elif not(bb.is_full_spurious()): if bb.nb_preds() == 1: bb_pred = list(bb.preds())[0] print " One pred! %d, 0x%x" % (bb_pred.nb_succs(), bb_pred.last()) if bb_pred.nb_succs() == 1 and bb_pred.last() in po_addrs: bb_pred.concat(bb) cfg.pop(idx) else: print " None of all ?" cfg.Show() def stop_button_clicked(self): self.STOP = True def set_progress_visible(self, enable): self.loading_stat.setVisible(enable) self.progressbar_loading.setVisible(enable) self.stop_button.setVisible(enable) def make_progress_bar(self, parent): horizontalLayout_2 = QtWidgets.QHBoxLayout() horizontalLayout_2.setObjectName("horizontalLayout_2") self.loading_stat = QtWidgets.QLabel(parent) horizontalLayout_2.addWidget(self.loading_stat) self.progressbar_loading = QtWidgets.QProgressBar(parent) horizontalLayout_2.addWidget(self.progressbar_loading) self.stop_button = QtWidgets.QPushButton(parent) self.stop_button.setMaximumSize(QtCore.QSize(50, 30)) self.stop_button.setText("stop") horizontalLayout_2.addWidget(self.stop_button) self.stop_button.clicked.connect(self.stop_button_clicked) return horizontalLayout_2 def replace_var_bv_expr(self, name, sub, e, arr, pld): if isinstance(e, Bv): return e elif isinstance(e, Var): return sub if e.name == name else e elif isinstance(e, UnOp): return UnOp(e.op, self.replace_var_bv_expr(name, sub, e.expr, arr, pld), e.opt1, e.opt2) elif isinstance(e, BinOp): return BinOp(e.op, self.replace_var_bv_expr(name, sub, e.expr1, arr, pld), self.replace_var_bv_expr(name, sub, e.expr2, arr, pld)) elif isinstance(e, Ite): c1 = self.replace_var_bv_expr(name, sub, e.cond, arr, pld) e1 = self.replace_var_bv_expr(name, sub, e.expr1, arr, pld) e2 = self.replace_var_bv_expr(name, sub, e.expr2, arr, pld) return Ite(c1, e1, e2) elif isinstance(e, Select) or isinstance(e, Select32): if e.expr in arr: return arr[e.expr] else: new_v = pld.pop(0) # TODO: HERE ! arr[e.expr] = Var(new_v) return Var(new_v) else: print "Unknown type", type(e) return e def slice(self, f): cmp_found = False var_seen = set() off_to_keep = {} bin_op = None tmp_arr = {} bag = ['y', 'x', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'] for off in reversed(sorted(f.formula.keys())): # print "Offset ", off, ": ", if not cmp_found: if off == -1: break mnemonic = f.offset_instr[off][1] if mnemonic[:4] in ["cmp ", "test", "sub "]: # print "Found cmp %d: %s" % (off, f.offset_instr[off][1]) cmp_found = True vars = set(f.get_var_bv_expr(f.formula[off][0].value)) # print "vars found", vars if len(vars) != 2: # print "No two variables were found abort %s", vars break var_seen.update(vars) off_to_keep[off] = [0] bin_op = BinOp("bvcomp", Var(vars.pop()), Var(vars.pop())) else: continue else: # Normal case cmds = f.formula[off] for i, vardef in reversed(zip(xrange(len(cmds)), cmds)): try: name, expr = vardef.name, vardef.value if name in var_seen: var_seen.remove(name) vars = f.get_var_bv_expr(expr) # print name, " found vars:", vars var_seen.update(vars) off_to_keep[off] = off_to_keep.get(off, [])+[i] bin_op = self.replace_var_bv_expr(name, expr, bin_op, tmp_arr, bag) else: pass # print name, " does not belong to var_seen" except AttributeError: pass if not var_seen: # Means empty break bin_op = self.replace_var_bv_expr("stub", "stub", bin_op, tmp_arr, bag) if bin_op is not None else None addr_dep = [v[0] for k, v in f.offset_instr.items() if k in off_to_keep] distance = max(off_to_keep)-min(off_to_keep)+1 if len(off_to_keep) != 0 else 0 return addr_dep, bin_op, distance def expr_synthesis(self, e, top=True): o, r = ("(", ")") if not top else ("", "") if isinstance(e, Bv): return e.value elif isinstance(e, Var): return e.name elif isinstance(e, UnOp): if e.op in ["zero_extend", "sign_extend"]: return self.expr_synthesis(e.expr, False) elif e.op == "extract": s = self.expr_synthesis(e.expr, True) return u"(%s){%s,%s}" % (s, e.opt2, e.opt1) if not top else s else: return u"(%s %s)" % (e.op, self.expr_synthesis(e.expr, False)) elif isinstance(e, BinOp): if e.op == "bvmul" and e.expr1 == e.expr2: return u"%s²" % (self.expr_synthesis(e.expr1, True)) elif e.op == "bvxor" and e.expr1 == e.expr2: return u"0" elif e.op == "bvmul" and isinstance(e.expr2, Bv): return u"%s%s" % (e.expr2.value, self.expr_synthesis(e.expr1, False)) else: top = e.op == "bvcomp" and top return u"%s%s %s %s%s" % (o, self.expr_synthesis(e.expr1, top), SMTFormula.bop_to_pp_string(e.op), self.expr_synthesis(e.expr2, top), r) elif isinstance(e, Ite): c = self.expr_synthesis(e.cond, False) e1 = self.expr_synthesis(e.expr1, False) e2 = self.expr_synthesis(e.expr2, False) return u"%s%s ? %s: %s%s" % (o, c, e1, e2, r) else: return u"[n/a]" def generate_dead_alive_dump(self): f = Path("dead_or_alive_dump.txt") handle = f.open("w") for cfg in self.functions_cfg.values(): for bb in cfg.values(): for i in bb.instrs: status = bb.instrs_status[i] if bb.is_alive() else Status.DEAD size = idc.NextHead(i)-i handle.write(u"%x,%d,%s\n" % (i, size, status)) handle.close()