def relational_analysis_cmd(args: argparse.Namespace) -> NoReturn: # arguments xname1: str = args.xname1 xname2: str = args.xname2 xfunctions1: List[str] = args.functions1 xfunctions2: List[str] = args.functions2 showfunctions: bool = args.functions showinstructions: bool = args.instructions usermappingfile: Optional[str] = args.usermapping callees: List[str] = args.callees usermapping: Dict[str, str] = {} if usermappingfile is not None: if os.path.isfile(usermappingfile): with open(usermappingfile, "r") as fp: userdata = json.load(fp) usermapping = userdata["function-mapping"] else: UC.print_error("Usermapping file " + usermappingfile + " not found") exit(1) try: (path1, xfile1) = UC.get_path_filename(xname1) UF.check_analysis_results(path1, xfile1) except UF.CHBError as e: print(str(e.wrap())) exit(1) xinfo1 = XI.XInfo() xinfo1.load(path1, xfile1) try: (path2, xfile2) = UC.get_path_filename(xname2) UF.check_analysis_results(path2, xfile2) except UF.CHBError as e: print(str(e.wrap())) exit(1) xinfo2 = XI.XInfo() xinfo2.load(path2, xfile2) app1 = UC.get_app(path1, xfile1, xinfo1) app2 = UC.get_app(path2, xfile2, xinfo2) relanalysis = RelationalAnalysis(app1, app2, faddrs1=xfunctions1, faddrs2=xfunctions2, usermapping=usermapping, callees=callees) print(relanalysis.report(showfunctions, showinstructions)) exit(0)
def report_calls(args: argparse.Namespace) -> NoReturn: # arguments xname: str = args.xname try: (path, xfile) = UC.get_path_filename(xname) UF.check_analysis_results(path, xfile) except UF.CHBError as e: print(str(e.wrap())) exit(1) xinfo = XI.XInfo() xinfo.load(path, xfile) app = UC.get_app(path, xfile, xinfo) calls = app.call_instructions() calllist: List[Tuple[str, str]] = [] for faddr in sorted(calls): print("\nFunction " + faddr) for baddr in calls[faddr]: print(" Block " + baddr) for instr in calls[faddr][baddr]: calllist.append((instr.annotation, faddr)) print(" " + str(instr)) print("\nCalls aggregated") print("------------------") for (c, faddr) in sorted(calllist): print(faddr.ljust(12) + c) exit(0)
def check_test(self, r) -> None: testfilename = UF.get_test_filename("arm32", "elf", "DT", r[0]) try: (path, xfile) = UC.get_path_filename(testfilename) except UF.CHBError as e: print(str(e.wrap())) exit(1) xinfo = XI.XInfo() xinfo.load(path, xfile) app = UC.get_app(path, xfile, xinfo) if app.has_function("0x1000"): f = app.function("0x1000") instr = list(f.instructions.values())[0] mnemonic = instr.mnemonic ops = instr.operandstring refmnemonic = r[2] refopstring = r[3] if mnemonic != refmnemonic: print("Mismatch in mnemonic: " + mnemonic + ", " + refmnemonic) if ops != refopstring: print("Mismatch in operandstring: " + ops + ", " + refopstring) else: print( "[" + r[0].ljust(8) + r[1].ljust(10) + r[2].ljust(8) + r[3].ljust(12) + "ok]")
def pedatacmd(args: argparse.Namespace) -> NoReturn: # arguments xname: str = args.xname headeronly: bool = args.headeronly imports: bool = args.imports headers: bool = args.headers sections: bool = args.sections section: Optional[str] = args.section try: (path, xfile) = UC.get_path_filename(xname) UC.prepare_executable(path, xfile, doreset=False, doresetx=False, hints=[]) except UF.CHBError as e: print(str(e.wrap())) exit(1) xinfo = XI.XInfo() xinfo.load(path, xfile) if not xinfo.is_pe32: UC.print_error("File is not a PE32 file: " + xinfo.format) exit(1) app = UC.get_app(path, xfile, xinfo) peheader = app.header if headeronly: print(peheader) exit(0) if imports: for i in list(peheader.import_tables.values()): print(str(i)) exit(0) if headers: for h in list(peheader.section_headers.values()): print(str(h)) exit(0) if sections: for (name, s) in peheader.sections.items(): print(str(s)) exit(0) if section is not None: s = peheader.get_section(section) if s is None: UC.print_error("Unable to find section at virtual address: " + str(section)) exit(1) print(str(s)) exit(0) print(peheader) for i in list(peheader.import_tables.values()): print(str(i)) for h in list(peheader.section_headers.values()): print(str(h)) exit(0)
def elfdatacmd(args: argparse.Namespace) -> NoReturn: # arguments xname: str = args.xname savesectionheaders: str = args.save_section_headers showlibs: bool = args.libs showheader: bool = args.header try: (path, xfile) = UC.get_path_filename(xname) UC.prepare_executable(path, xfile, doreset=False, doresetx=False, hints=[]) except UF.CHBError as e: print(str(e.wrap())) exit(1) xinfo = XI.XInfo() xinfo.load(path, xfile) if not xinfo.is_elf: print("File is not an ELF file: " + xinfo.format) exit(1) app = UC.get_app(path, xfile, xinfo) elfheader = app.header try: if showheader: print(elfheader.fileheaderstr()) elif showlibs: if elfheader.has_dynamic_table(): for s in elfheader.get_dynamic_table().dynamic_libraries: print(" " + s) else: print("=") print("Binary does not have a dynamic table") print("=") else: print(str(elfheader)) except UF.CHBError as e: print(str(e.wrap())) exit(1) if savesectionheaders: result: Dict[str, Any] = {} result["file"] = xinfo.file result["md5"] = xinfo.md5 result["section-headers"] = [] for s in elfheader.sectionheaders: result["section-headers"].append(s.attribute_values()) filename = xname + "_section_headers.json" with open(filename, "w") as fp: json.dump(result, fp, indent=3) UC.print_info("Saved section headers in " + filename) exit(0)
def load_mips_lib_file(libxname: str) -> Tuple["MIPSAccess", "MIPSAssembly"]: try: (libpath, libxfile) = UC.get_path_filename(libxname) UF.check_analysis_results(libpath, libxfile) except UF.CHBError as e: print(str(e.wrap())) exit(1) libxinfo = XI.XInfo() libxinfo.load(libpath, libxfile) libapp = cast("MIPSAccess", UC.get_app(libpath, libxfile, libxinfo)) libasm = cast("MIPSAssembly", UC.get_asm(libapp)) return (libapp, libasm)
def check_test_case(filename: str) -> int: testdatafile = filename + ".json" try: with open(testdatafile, "r") as fp: testdata = json.load(fp) except Exception as e: print(str(e)) return (-1) try: (path, xfile) = UC.get_path_filename(filename) UF.check_analysis_results(path, xfile) except UF.CHBError as e: print(str(e.wrap())) exit(1) xinfo = XI.XInfo() xinfo.load(path, xfile) app = UC.get_app(path, xfile, xinfo) for faddr in testdata["functions"]: if app.has_function(faddr): f = app.function(faddr) fstats = app.result_metrics.get_function_metrics(faddr) result = check_test_function(f, fstats, testdata["functions"][faddr]) if result != 0: break else: print("Function " + faddr + " not found in results") result = (-1) break else: result = 0 return result
def report_memops(args: argparse.Namespace) -> NoReturn: # arguments xname: str = args.xname try: (path, xfile) = UC.get_path_filename(xname) UF.check_analysis_results(path, xfile) except UF.CHBError as e: print(str(e.wrap())) exit(1) xinfo = XI.XInfo() xinfo.load(path, xfile) app = UC.get_app(path, xfile, xinfo) memloads = app.load_instructions() memstores = app.store_instructions() loadstats: Dict[str, Tuple[int, int]] = {} storestats: Dict[str, Tuple[int, int]] = {} def add_load_unknown(faddr: str) -> None: loadstats.setdefault(faddr, (0, 0)) v = loadstats[faddr] loadstats[faddr] = (v[0] + 1, v[1]) def add_load_known(faddr: str) -> None: loadstats.setdefault(faddr, (0, 0)) v = loadstats[faddr] loadstats[faddr] = (v[0] + 1, v[1] + 1) loadxrefs: Dict[str, List[str]] = {} def add_load_xref(gv: str, faddr: str) -> None: loadxrefs.setdefault(gv, []) loadxrefs[gv].append(faddr) print("Load instructions") print("-----------------") for faddr in sorted(memloads): print("\nFunction " + faddr) for baddr in memloads[faddr]: print(" Block: " + baddr) for instr in memloads[faddr][baddr]: print(" " + str(instr)) for rhs in instr.rhs: add_load_xref(str(rhs), faddr) if str(rhs) == "?": add_load_unknown(faddr) else: add_load_known(faddr) def add_store_unknown(faddr: str) -> None: storestats.setdefault(faddr, (0, 0)) v = storestats[faddr] storestats[faddr] = (v[0] + 1, v[1]) def add_store_known(faddr: str) -> None: storestats.setdefault(faddr, (0, 0)) v = storestats[faddr] storestats[faddr] = (v[0] + 1, v[1] + 1) storexrefs: Dict[str, List[str]] = {} def add_store_xref(gv: str, faddr: str) -> None: storexrefs.setdefault(gv, []) storexrefs[gv].append(faddr) print("\n\nStore instructions") print("----------------------") for faddr in sorted(memstores): print("\nFunction " + faddr) for baddr in memstores[faddr]: print(" Block: " + baddr) for instr in memstores[faddr][baddr]: print(" " + str(instr)) for lhs in instr.lhs: add_store_xref(str(lhs), faddr) if str(lhs) in ["?", "??operand??"]: add_store_unknown(faddr) else: add_store_known(faddr) print("\nLoad xreferences") print("------------------") for gv in sorted(loadxrefs): if gv.startswith("gv_"): print(gv.ljust(24) + "[" + ", ".join(loadxrefs[gv]) + "]") print("\nStore xreferences") print("-------------------") for gv in sorted(storexrefs): if gv.startswith("gv"): print(gv.ljust(24) + "[" + ", ".join(storexrefs[gv]) + "]") print("\nLoad statistics") print("-----------------") loadtotal: int = 0 loadknown: int = 0 for faddr in sorted(loadstats): ftotal = loadstats[faddr][0] fknown = loadstats[faddr][1] loadtotal += ftotal loadknown += fknown print(faddr + ": " + str(fknown).rjust(4) + " / " + str(ftotal).ljust(4)) perc = (loadknown / loadtotal) * 100 fperc = "{:.2f}".format(perc) print("\nTotal: " + str(loadknown) + " / " + str(loadtotal) + " (" + fperc + "%)") print("\nStore statistics") print("------------------") storetotal = 0 storeknown = 0 for faddr in sorted(storestats): ftotal = storestats[faddr][0] fknown = storestats[faddr][1] storetotal += ftotal storeknown += fknown print(faddr + ": " + str(fknown).rjust(4) + " / " + str(ftotal).ljust(4)) perc = (storeknown / storetotal) * 100 fperc = "{:.2f}".format(perc) print("\nTotal: " + str(storeknown) + " / " + str(storetotal) + " (" + fperc + "%)") exit(0)
def simulate_function_cmd(args: argparse.Namespace) -> NoReturn: # arguments xname: str = args.xname faddr: str = args.faddr stepcount: int = args.steps libs: List[str] = args.libs support: Optional[str] = args.support stub_imports: List[str] = args.stub_imports mainargs: List[str] = args.mainargs optargaddrstr: Optional[str] = args.optargaddr optargstatestr: Optional[str] = args.optargstate patched_globals: List[str] = args.patched_globals envptr_addr: Optional[str] = args.envptr_addr mainargs = [a.strip() for a in mainargs] try: (path, xfile) = UC.get_path_filename(xname) UF.check_analysis_results(path, xfile) except UF.CHBError as e: print(str(e.wrap())) exit(1) libnames = unpack_named_strings(libs) libapps: Dict[str, Tuple["MIPSAccess", "MIPSAssembly"]] = {} for (name, libxname) in libnames.items(): libapps[name] = load_mips_lib_file(libxname) xinfo = XI.XInfo() xinfo.load(path, xfile) print(str(xinfo)) app = UC.get_app(path, xfile, xinfo) asm = UC.get_asm(app) if xinfo.is_mips: app = cast("MIPSAccess", app) asm = cast("MIPSAssembly", asm) simulate_mips_function( xname, app, asm, faddr, stepcount=stepcount, libs=libapps, support=support, stub_imports=stub_imports, mainargs=[x.lstrip() for x in mainargs], optargaddrstr=optargaddrstr, optargstatestr=optargstatestr, patched_globals=unpack_named_strings(patched_globals), envptr_addr=envptr_addr) elif xinfo.is_arm: app = cast("ARMAccess", app) asm = cast("ARMAssembly", asm) simulate_arm_function(xname, app, asm, faddr) else: UC.print_error( "Simulation not yet implemented for " + app.__class__.__name__) exit(1)
def run_commands(args: argparse.Namespace) -> NoReturn: # arguments name: str = args.cname maxp: int = args.maxp targets: List[str] = args.targets showtargets: bool = args.showtargets if not (os.path.isfile(name)): UC.print_error( "Please specify a json file that lists the analysis commands.") exit(1) try: with open(name, "r") as fp: cmdsfile = json.load(fp) except json.decoder.JSONDecodeError as e: UC.print_error("Error in json commands file: " + str(e)) exit(1) if "targets" not in cmdsfile: UC.print_error( "Please provide a list of targets with analysis arguments.") exit(1) cmdtargets = cmdsfile["targets"] if showtargets: print("Targets available:") print("-" * 80) for tgt in cmdtargets: print(" " + tgt) print("-" * 80) exit(0) results: Dict[str, List[Tuple[List[str], int]]] = {} for tgt in targets: print("Target: " + tgt) tgts = cmdtargets[tgt] if "cmd" not in tgts: UC.print_error("Please specify a command (with cmd) for target " + tgt) exit(1) if "instances" not in tgts: UC.print_error("Please specify instances for target " + tgt) exit(1) cmd = tgts["cmd"] cmdinstances = tgts["instances"] cmdlines: List[Tuple[Optional[str], List[str]]] = [] for cmdinst in cmdinstances: cmdline = (cmdinst.get("output", None), cmd + cmdinst["args"]) cmdlines.append(cmdline) pool = Pool(maxp) with timing(tgt): results[tgt] = pool.map(do_cmd, cmdlines) if "collect" in tgts: collectitems = tgts["collect"] collecteddata: Dict[str, Dict[str, int]] = {} unknowns: Dict[str, Dict[str, int]] = {} opcode_distribution: Dict[str, int] = {} filenames: List[str] = [ cmdinst["args"][0] for cmdinst in tgts["instances"] ] for filename in filenames: (path, xfile) = UC.get_path_filename(filename) xinfo = XI.XInfo() xinfo.load(path, xfile) app = UC.get_app(path, xfile, xinfo) asm = UC.get_asm(app) if "opcode-distribution" in collectitems: opcd: Dict[str, int] = asm.opcode_distribution() for (k, v) in opcd.items(): opcode_distribution.setdefault(k, 0) opcode_distribution[k] += v if "unknowns" in collectitems and xinfo.is_arm: for instr in asm.unknown_instructions: instr = cast("ARMAssemblyInstruction", instr) if instr.mnemonic == "unknown": hint = instr.unknown_hint() unknowns.setdefault(hint, {}) unknowns[hint].setdefault(xfile, 0) unknowns[hint][xfile] += 1 if "output" in tgts: outputfilename = tgts["output"] opcode_output: Dict[str, Any] = {} opcode_output["name"] = tgt opcode_output["opcode-distribution"] = opcode_distribution opcode_output["unknowns"] = unknowns with open(outputfilename, "w") as fp: json.dump(opcode_output, fp, indent=2) else: print("\nOpcode distribution") for (opc, c) in sorted(opcode_distribution.items()): print(str(c).rjust(10) + " " + opc) print("\nUnknowns") for (hint, ufiles) in sorted(unknowns.items()): print("\n" + hint) for (f, c) in sorted(ufiles.items()): print(" " + str(c).rjust(5) + " " + f) for tgt in results: print("\nResults for " + tgt) for fresult in results[tgt]: status = " ok " if fresult[1] == 0 else "fail" print(status + " " + " ".join(fresult[0])) exit(0)
def remove_test(self, r) -> None: testfilename = UF.get_test_filename("arm32", "elf", "DT", r[0]) (path, xfile) = UC.get_path_filename(testfilename) os.remove(os.path.join(path, xfile)) shutil.rmtree(os.path.join(path, xfile + ".ch"))