def run_query(qf, ea_list, qs): subtitle = qs.help title = subtitle if len(subtitle) < 80 else "%s..." % subtitle[:77] ch = hxtb.ic_t(title="Shell [%s]" % title) mode = qs.ast_type == 1 idaapi.show_wait_box("Processing") try: nfuncs = len(ea_list) for j, ea in enumerate(ea_list): if idaapi.user_cancelled(): break idaapi.replace_wait_box("Processing function %d/%d" % (j + 1, nfuncs)) r = list() try: r = hxtb.exec_query(qf, [ea], mode, parents=True, flags=idaapi.DECOMP_NO_WAIT) for x in r: ch.append(x) except Exception as e: print("%s: %s" % (SCRIPT_NAME, e)) finally: idaapi.hide_wait_box() return ch
def __call__(self): if idaapi.user_cancelled(): bc_log.info("Analysis cancelled by user, terminating process") self.qprocess.terminate() return -1 else: return self.interval
def retrieve_selected_functions(self, funcs): if not self.check_before_use(): return funcset_ids = [self.funcset] if not self.cfg['usepublic'] else None i, succ, skip, fail = 0, 0, 0, 0 _funcs = [ea for ea in funcs] funcs_len = len(_funcs) idaapi.show_wait_box("Matching... (0/{})".format(funcs_len)) for ea in _funcs: i += 1 idaapi.replace_wait_box("Matching... ({}/{})".format(i, funcs_len)) if idaapi.user_cancelled(): idaapi.hide_wait_box() print( "[{}] {} functions successfully matched, {} functions failed, {} functions skipped" .format(self.name, succ, fail, skip)) return code = self.retrieve_function_with_check(ea, 1, funcset_ids) if code == 0: succ += 1 elif code == 1: skip += 1 else: fail += 1 idaapi.hide_wait_box() print( "[{}] {} functions successfully matched, {} functions failed, {} functions skipped" .format(self.name, succ, fail, skip))
def match_funcs(self, funcs): if not self.check_before_use(): return i, fail, skip, succ = 0, 0, 0, 0 def stop(): idaapi.hide_wait_box() BinaryAILog.summary(succ, skip, fail, "matched") funcs_len = len(funcs) idaapi.show_wait_box("Matching... (0/{})".format(funcs_len)) for ea in funcs: # refresh process status i += 1 idaapi.replace_wait_box("Matching... ({}/{})".format(i, funcs_len)) # check cancelled or not if idaapi.user_cancelled(): stop() return status = None try: status = self._match_with_check(ea) finally: if status == 1: succ += 1 elif status == 0: skip += 1 else: fail += 1 stop()
def ghidraaas_checkin(bin_file_path, filename, ghidra_server_url): """ Upload the .bytes files in ghidraaas. One time only (until IDA is restarted...) """ idaapi.show_wait_box("Connecting to Ghidraaas. Sending bytes file...") try: md5_hash = idautils.GetInputFileMD5() queue = Queue.Queue() my_args = (bin_file_path, filename, ghidra_server_url, md5_hash, queue) t1 = threading.Thread(target=ghidraaas_checkin_thread, args=my_args) t1.start() counter = 0 stop = False while not stop: time.sleep(SLEEP_LENGTH) counter += 1 # User terminated action if idaapi.user_cancelled(): stop = True print("GhIDA:: [!] Check-in interrupted.") continue # Reached TIIMEOUT if counter > COUNTER_MAX: stop = True print("GhIDA:: [!] Timeout reached.") continue # Thread terminated if not t1.isAlive(): stop = True print("GhIDA:: [DEBUG] Thread terminated.") continue print("GhIDA:: [DEBUG] Joining check-in thread.") t1.join(0) q_result = queue.get_nowait() print("GhIDA:: [DEBUG] Thread joined. Got queue result.") idaapi.hide_wait_box() return q_result except Exception: idaapi.hide_wait_box() print("GhIDA:: [!] Check-in error.") idaapi.warning("GhIDA check-in error") return False
def ghidraaas_checkout(ghidra_server_url): """ That's all. Remove .bytes file from Ghidraaas server. """ if not GLOBAL_CHECKIN: return idaapi.show_wait_box( "Connecting to Ghidraaas. Removing temporary files...") try: md5_hash = idautils.GetInputFileMD5() aargs = (md5_hash, ghidra_server_url) t1 = threading.Thread(target=ghidraaas_checkout_thread, args=aargs) t1.start() counter = 0 stop = False while not stop: time.sleep(SLEEP_LENGTH) counter += 1 if idaapi.user_cancelled(): print("GhIDA:: [!] Check-out interrupted.") stop = True continue if counter > COUNTER_MAX: print("GhIDA:: [!] Timeout reached.") stop = True continue if not t1.isAlive(): stop = True print("GhIDA:: [DEBUG] Thread terminated.") continue print("GhIDA:: [DEBUG] Joining check-out thread.") t1.join(0) print("GhIDA:: [DEBUG] Thread joined") idaapi.hide_wait_box() return except Exception: idaapi.hide_wait_box() print("GhIDA:: [!] Check-out error") idaapi.warning("GhIDA check-out error") return
def upload_funcs(self, funcs): if not self.check_before_use(check_funcset=True): return i, succ, skip, fail = 0, 0, 0, 0 def stop(): idaapi.hide_wait_box() BinaryAILog.summary(succ, skip, fail, "uploaded") funcs_len = len(funcs) idaapi.show_wait_box("Uploading... (0/{})".format(funcs_len)) for ea in funcs: i += 1 idaapi.replace_wait_box("Uploading... ({}/{})".format( i, funcs_len)) if idaapi.user_cancelled(): stop() return # < minsize pfn = idaapi.get_func(ea) if idaapi.FlowChart(pfn).size < bai_config['minsize']: skip += 1 continue # try upload func_id = None try: func_id = self.mgr.upload(ea, self.mgr.funcset) except DecompilationFailure as e: BinaryAILog.fail(idaapi.get_func_name(ea), str(e)) fail += 1 continue except BinaryAIException as e: stop() BinaryAILog.fatal(e) # fail if not func_id: fail += 1 continue succ += 1 stop()
def upload_selected_functions(self, funcs): if not self.check_before_use(check_funcset=True): return i, succ, skip, fail = 0, 0, 0, 0 _funcs = [ea for ea in funcs] funcs_len = len(_funcs) idaapi.show_wait_box("Uploading... (0/{})".format(funcs_len)) for ea in _funcs: i += 1 idaapi.replace_wait_box("Uploading... ({}/{})".format( i, funcs_len)) if idaapi.user_cancelled(): idaapi.hide_wait_box() print( "[{}] {} functions successfully uploaded, {} functions failed, {} functions skipped" .format(self.name, succ, fail, skip)) return pfn = idaapi.get_func(ea) if idaapi.FlowChart(pfn).size < self.cfg['minsize']: skip += 1 continue func_id = None try: func_id = self.upload_function(ea, self.funcset) except DecompilationFailure: pass except BinaryAIException as e: idaapi.hide_wait_box() assert False, "[BinaryAI] {}".format(e._msg) func_name = idaapi.get_func_name(ea) if not func_id: print("[{}] {} failed because upload error".format( self.name, func_name)) fail += 1 continue succ += 1 idaapi.hide_wait_box() print( "[{}] {} functions successfully uploaded, {} functions failed, {} functions skipped" .format(self.name, succ, fail, skip))
def revert_funcs(self, funcs): i, succ, skip = 0, 0, 0 def stop(): idaapi.hide_wait_box() BinaryAILog.summary(succ, skip, 0, "reverted") funcs_len = len(funcs) idaapi.show_wait_box("Reverting... (0/{})".format(funcs_len)) for ea in funcs: i += 1 idaapi.replace_wait_box("Reverting... ({}/{})".format(i, funcs_len)) if idaapi.user_cancelled(): stop() return if bai_mark.revert_bai_func(ea): succ += 1 else: skip += 1 stop()
idaapi.replace_wait_box("Total progress: %s\n\nScanning: %s\n\n" % (progress, funcname)) try: cfunc = idaapi.decompile(ea, flags=idaapi.DECOMP_NO_WAIT) except idaapi.DecompilationFailure: print "Error decompiling function @ 0x%x" % ea cfunc = None if cfunc: fp = func_parser_t(cfunc) fp.apply_to(cfunc.body, None) choser.feed(fp.data) if idaapi.user_cancelled(): aborted = True break i += 1 idaapi.hide_wait_box() if aborted: idaapi.warning("Aborted.") # IDA <= 7.2 else: for ea in func_list: try: cfunc = idaapi.decompile(ea) except idaapi.DecompilationFailure: print "Error decompiling function @ 0x%x" % ea
def analysis_finish_cb(self, outfname, logfname, cfaoutfname, ea=None): idaapi.show_wait_box("HIDECANCEL\nParsing BinCAT analysis results") bc_log.debug("Parsing analyzer result file") # Here we can't check for user_cancelled because the UI is # unresponsive when parsing. try: cfa = cfa_module.CFA.parse(outfname, logs=logfname) except (pybincat.PyBinCATException, NoSectionError): idaapi.hide_wait_box() bc_log.error("Could not parse result file") return None self.clear_background() self.cfa = cfa if cfa: # XXX add user preference for saving to idb? in that case, store # reference to marshalled cfa elsewhere bc_log.info("Storing analysis results to idb...") with open(outfname, 'rb') as f: self.netnode["out.ini"] = f.read() with open(logfname, 'rb') as f: self.netnode["analyzer.log"] = f.read() if self.remapped_bin_path: self.netnode["remapped_bin_path"] = self.remapped_bin_path self.netnode["remap_binary"] = self.remap_binary if cfaoutfname is not None and os.path.isfile(cfaoutfname): with open(cfaoutfname, 'rb') as f: self.last_cfaout_marshal = f.read() bc_log.info("Analysis results have been stored idb.") else: bc_log.info("Empty or unparseable result file.") bc_log.debug("----------------------------") idaapi.replace_wait_box("Updating IDB with BinCAT results") # Update current RVA to start address (nodeid = 0) # by default, use current ea - e.g, in case there is no results (cfa is # None) or no node 0 (happens in backward mode) current_ea = self.current_ea if ea is not None: current_ea = ea else: try: node0 = cfa['0'] if node0: current_ea = node0.address.value except (KeyError, TypeError): # no cfa is None, or no node0 pass try: self.set_current_ea(current_ea, force=True) except TypeError as e: bc_log.warn("Could not load results from IDB") bc_log.warn("------ BEGIN EXCEPTION -----") bc_log.exception(e) bc_log.warn("------ END EXCEPTION -----") idaapi.hide_wait_box() return None self.netnode["current_ea"] = current_ea if not cfa: return for addr, nodeids in cfa.addr_nodes.items(): if hasattr(idaapi, "user_cancelled") and idaapi.user_cancelled() > 0: bc_log.info("User cancelled!") idaapi.hide_wait_box() return None ea = addr.value tainted = False taint_id = 1 for n_id in nodeids: # is it tainted? # find child nodes node = cfa[n_id] if node.tainted: tainted = True if node.taintsrc: # Take the first one taint_id = int(node.taintsrc[0].split("-")[1]) break if tainted: idaapi.set_item_color(ea, taint_color(taint_id)) else: idaapi.set_item_color(ea, 0xF0F0F0) idaapi.hide_wait_box() self.gui.focus_registers()
def ghidraaas_decompile(address, xml_file_path, bin_file_path, ghidra_server_url): """ Send the xml file to ghidraaas and ask to decompile a function """ global GLOBAL_CHECKIN # Filename without the .xml extension filename = GLOBAL_FILENAME if not GLOBAL_CHECKIN: if ghidraaas_checkin(bin_file_path, filename, ghidra_server_url): GLOBAL_CHECKIN = True else: raise Exception("[!] Ghidraaas Check-in error") idaapi.show_wait_box( "Connecting to Ghidraaas. Decompiling function %s" % address) try: md5_hash = idautils.GetInputFileMD5() queue = Queue.Queue() aargs = (address, xml_file_path, bin_file_path, ghidra_server_url, filename, md5_hash, queue) t1 = threading.Thread(target=ghidraaas_decompile_thread, args=aargs) t1.start() counter = 0 stop = False while not stop: time.sleep(SLEEP_LENGTH) counter += 1 if idaapi.user_cancelled(): print("GhIDA:: [!] decompilation interrupted.") stop = True continue if counter > COUNTER_MAX: print("GhIDA:: [!] Timeout reached.") stop = True continue if not t1.isAlive(): stop = True print("GhIDA:: [DEBUG] Thread terminated.") continue print("GhIDA:: [DEBUG] Joining decompilation thread.") t1.join(0) q_result = queue.get_nowait() print("GhIDA:: [DEBUG] Thread joined. Got queue result.") idaapi.hide_wait_box() return q_result except Exception: idaapi.hide_wait_box() print("GhIDA:: [!] Unexpected decompilation error") idaapi.warning("GhIDA decompilation error") return None
def ghidra_headless(address, xml_file_path, bin_file_path, ghidra_headless_path, ghidra_plugins_path): """ Call Ghidra in headless mode and run the plugin FunctionDecompile.py to decompile the code of the function. """ try: if not os.path.isfile(ghidra_headless_path): print("GhIDA:: [!] ghidra analyzeHeadless not found.") raise Exception("analyzeHeadless not found") decompiled_code = None idaapi.show_wait_box("Ghida decompilation started") prefix = "%s_" % address output_temp = tempfile.NamedTemporaryFile(prefix=prefix, delete=False) output_path = output_temp.name # print("GhIDA:: [DEBUG] output_path: %s" % output_path) output_temp.close() cmd = [ghidra_headless_path, ".", "Temp", "-import", xml_file_path, '-readOnly', '-scriptPath', ghidra_plugins_path, '-postScript', 'FunctionDecompile.py', address, output_path, "-noanalysis", "-deleteProject"] # Options to 'safely' terminate the process if os.name == 'posix': kwargs = { 'preexec_fn': os.setsid } else: kwargs = { 'creationflags': subprocess.CREATE_NEW_PROCESS_GROUP, 'shell': True } p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs) stop = False counter = 0 print("GhIDA:: [INFO] Ghidra headless (timeout: %ds)" % TIMEOUT) print("GhIDA:: [INFO] Waiting Ghidra headless analysis to finish...") while not stop: time.sleep(SLEEP_LENGTH) counter += 1 subprocess.Popen.poll(p) # Process terminated if p.returncode is not None: stop = True print("GhIDA:: [INFO] Ghidra analysis completed!") continue # User terminated action if idaapi.user_cancelled(): # Termiante the process! terminate_process(p.pid) stop = True print("GhIDA:: [!] Ghidra analysis interrupted.") continue # Process timeout if counter > COUNTER_MAX: terminate_process(p.pid) stop = True print("GhIDA:: [!] Decompilation error - timeout reached") continue # Check if JSON response is available if os.path.isfile(output_path): with open(output_path) as f_in: j = json.load(f_in) if j['status'] == "completed": decompiled_code = j['decompiled'] else: print("GhIDA:: [!] Decompilation error -", " JSON response is malformed") # Remove the temporary JSON response file os.remove(output_path) else: print("GhIDA:: [!] Decompilation error - JSON response not found") idaapi.warning("Ghidra headless decompilation error") except Exception as e: print("GhIDA:: [!] %s" % e) print("GhIDA:: [!] Ghidra headless analysis failed") idaapi.warning("Ghidra headless analysis failed") decompiled_code = None finally: idaapi.hide_wait_box() return decompiled_code