def load(self): plugin_menu = self.main.getMenuByType(cutter.MainWindow.MenuType.Plugins) action = QtWidgets.QAction("x64dbg - Import database", self.main) action.triggered.connect(self.import_db) plugin_menu.addAction(action) action = QtWidgets.QAction("x64dbg - Export database", self.main) action.triggered.connect(self.export_db) plugin_menu.addAction(action) cutter.message("[w64dbg-cutter] Initialized")
def export_db(self): filename = self.file_dialog("Open new file for export", True) if not filename: return cutter.message("[x64dbg-cutter]: Exporting database to %s" % filename) db = {} base_addr = cutter.cmdj("elJ bin.baddr")[0]["value"] cutter.message("[x64dbg-cutter]: baddr is %d" % base_addr) # We can only export items from the main binary currently module = os.path.basename(cutter.cmdj("ij")["core"]["file"]).lower() # ref: {"addr":5368778842,"size":1,"prot":"--x","hw":false,"trace":false,"enabled":true,"data":"","cond":""} db["breakpoints"] = [{ "address": hex(bp["addr"] - base_addr), # Comment address relative to the base of the module "enabled": bp["enabled"], # Whether the breakpoint is enabled "type": BPHARDWARE if bp["hw"] else BPNORMAL, # see https://github.com/x64dbg/x64dbg/blob/development/src/dbg/breakpoint.h#L13 "module": module, # The module that the comment is in } for bp in cutter.cmdj("dbj")] cutter.message("[x64dbg-cutter]: %d breakpoint(s) exported" % len(db["breakpoints"])) # ref: {"offset":5368713216,"type":"CCu","name":"[00] -rwx section size 65536 named .textbss"} db["comments"] = [{ "module": module, # The module that the comment is in "address": hex(c["offset"] - base_addr), # Comment address relative to the base of the module "manual": True, # Whether the comment was created by the user - set to True to show it in the comments window "text": c["name"], # Comment text } for c in cutter.cmdj("CCj")] cutter.message("[x64dbg-cutter]: %d comment(s) exported" % len(db["comments"])) # Set flagspace to all before iterating over fj to show all of the flags cutter.cmd("fs *") # ref: {"name":"fcn.1400113de","size":5,"offset":5368779742} db["labels"] = [{ "module": module, # The module that the label is in "address": hex(label["offset"] - base_addr), # Label address relative to the base of the module "manual": False, # Whether the label was created by the user "text": label["name"], # Label text } for label in cutter.cmdj("fj") if (label["offset"] - base_addr) >= 0] cutter.message("[x64dbg-cutter]: %d labels(s) exported" % len(db["labels"])) with open(filename, "w") as outfile: json.dump(db, outfile, indent=1)
def load_architecture(self, name): # fix name name = name.lower() if name.startswith("x86"): name = "x86-64" if name.startswith("arm"): name = "arm" self.arch = name path = self.base_path dbpath = os.path.join(path, "archs", name + ".sql") if (not os.path.isfile(dbpath)): cutter.message("Manual not found for architecture: %s" % name) return False con = sq.connect(":memory:") con.text_factory = str con.executescript(open(dbpath).read()) cur = con.cursor() cur.execute("SELECT mnem, description FROM instructions") con.commit() rows = cur.fetchall() for row in rows: inst = row[0] lines = row[1].replace("\r\n", "\n").split("\n") self.inst_map[inst] = lines con.close() for (inst, data) in self.inst_map.items(): data = data[0] if (data[0:3] == "-R:"): ref = data[3:] if (ref in self.inst_map): self.inst_map[inst] = self.inst_map[ref] cutter.message("Manual loaded for architecture: %s" % name) return True
def file_dialog(self, title, new=False): file_dialog = QtWidgets.QFileDialog(self.main, title, self._last_directory, 'Databases (*.dd64)') if new: filename = file_dialog.getSaveFileName()[0] else: file_dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile) filename = file_dialog.getOpenFileName()[0] # Remember the last directory we were in (parsed from a selected file) # for the next time the user comes to load coverage files if filename: self._last_directory = os.path.dirname(filename) + os.sep cutter.message("[w64dbg-cutter] Received filename from file dialog:") cutter.message(" - %s" % filename) return filename
def file_dialog(self, title, new=False): if new: filename = QtWidgets.QFileDialog.getSaveFileName( self.main, title, self._last_directory, X64DBG_FILE_FILTERS)[0] # Append x64dbg's file prefix to the saved file # NOTE: This solution isn't ideal but preferred filename isn't available in # PySide's QtFileDialog constructor if not re.findall(".dd(64|32)", filename): bitness = cutter.cmdj("elJ asm.bits")[0]["value"] filename += f".dd{bitness}" else: filename = QtWidgets.QFileDialog.getOpenFileName( self.main, title, self._last_directory, X64DBG_FILE_FILTERS)[0] # Remember the last directory we were in (parsed from a selected file) # for the next time the user comes to load coverage files if filename: self._last_directory = os.path.dirname(filename) + os.sep cutter.message("[w64dbg-cutter] Received filename from file dialog:") cutter.message(" - %s" % filename) return filename
def handle_disassembler_action(self): # for actions in plugin menu Cutter sets data to address for current dissasembly line cutter.message("Dissasembly menu action callback 0x{:x}".format( self.disas_action.data()))
def handle_addressable_item_action(self): # for actions in plugin menu Cutter sets data to current item address submenu_action = self.addr_submenu.menuAction() cutter.message("Context menu action callback 0x{:x}".format( submenu_action.data()))
def log(self, msg): """log to cutter console @param msg: message to log """ cutter.message(f"[CAPAExplorer]: {msg}")
def log(msg): """log to cutter console @param msg: message to log """ cutter.message(f"[capa explorer]: {msg}")
def create_cutter_plugin(): try: return CAPAExplorerPlugin() except Exception as e: cutter.message(str(e))
def import_db(self): filename = self.file_dialog("Open x64dbg (Uncompressed) JSON database") if not filename: return cutter.message("[x64dbg-cutter]: Importing database %s" % filename) with open(filename) as dbdata: db = json.load(dbdata) # We can only import symbols for the main binary currently module = os.path.basename(cutter.cmdj("ij")["core"]["file"]).lower() base_addr = cutter.cmdj("evj bin.baddr")[0]["value"] count = 0 breakpoints = db.get("breakpoints", []) for bp in breakpoints: try: if bp["module"] != module: continue address = int(bp["address"], 16) + base_addr cutter.cmd("dbs " + str(address)) # Breakpoints created by dbs are enabled by default if not bp["enabled"]: cutter.cmd("dbd " + str(address)) count += 1 except: cutter.message("[x64dbg-cutter]: " + traceback.format_exc()) cutter.message("[x64dbg-cutter]: %d/%d breakpoints(s) imported" % (count, len(breakpoints))) count = 0 comments = db.get("comments", []) for comment in comments: try: if comment["module"] != module: continue address = int(comment["address"], 16) + base_addr text = base64.b64encode( comment["text"].encode("utf-8")).decode() cutter.cmd("CCu base64:" + text + " @ " + str(address)) count += 1 except: cutter.message("[x64dbg-cutter]: " + traceback.format_exc()) cutter.message("[x64dbg-cutter]: %d/%d comment(s) imported" % (count, len(comments))) # Create a new flagspace for x64dbg labels and bookmarks to allow easy removal cutter.cmd("fs x64dbgcutter.labels") count = 0 labels = db.get("labels", []) for label in labels: try: if label["module"] != module: continue address = int(label["address"], 16) + base_addr # Spaces don't show up in flags, use underscore instead text = label["text"].replace(" ", "_") cutter.cmd("f " + text + " @ " + str(address)) count += 1 except: cutter.message("[x64dbg-cutter]: " + traceback.format_exc()) cutter.message("[x64dbg-cutter]: %d/%d label(s) imported" % (count, len(labels))) cutter.cmd("fs x64dbgcutter.bookmarks") count = 0 bookmarks = db.get("bookmarks", []) for bookmark in bookmarks: try: if bookmark["module"] != module: continue address = int(bookmark["address"], 16) + base_addr cutter.cmd("f " + "bookmark_" + str(address) + " @ " + str(address)) count += 1 except: cutter.message("[x64dbg-cutter]: " + traceback.format_exc()) cutter.message("[x64dbg-cutter]: %d/%d bookmark(s) imported" % (count, len(bookmarks))) cutter.message("[x64dbg-cutter]: Done!")