def handle_edit_key(self, key): if key in ACTION_MAP: return ACTION_MAP[key](self) line = self.get_cur_line() if key == editor.KEY_ENTER: line = self.get_cur_line() log.info("Enter pressed: %s" % line) op_no = self.cur_operand_no(line) self.show_status("Enter pressed: %s, %s" % (self.col, op_no)) to_addr = None # No longer try to jump only to addresses in args, parse # textual representation below if False and isinstance(line, engine.DisasmObj): if op_no >= 0: o = line[op_no] to_addr = o.get_addr() if to_addr is None: o = line.get_operand_addr() if o: to_addr = o.get_addr() if to_addr is None: pos = self.col - line.LEADER_SIZE - len(line.indent) word = utils.get_word_at_pos(line.cache, pos) self.show_status("Enter pressed: %s, %s, %s" % (self.col, op_no, word)) to_addr = self.resolve_expr(word) if to_addr is None: self.show_status("Unknown address: %s" % word) return self.goto_addr(to_addr, from_addr=self.cur_addr_subno()) elif key == editor.KEY_ESC: if self.addr_stack: self.show_status("Returning") self.goto_addr(self.addr_stack.pop()) elif key == b"q": res = ACTION_OK if self.model.AS.changed: res = DConfirmation("There're unsaved changes. Quit?").result() if res == ACTION_OK: return editor.KEY_QUIT self.redraw() elif key == b"\x1b[5;5~": # Ctrl+PgUp self.goto_addr(self.model.AS.min_addr(), from_addr=line.ea) elif key == b"\x1b[6;5~": # Ctrl+PgDn self.goto_addr(self.model.AS.max_addr(), from_addr=line.ea) elif key == b"c": addr = self.cur_addr() self.show_status("Analyzing at %x" % addr) engine.add_entrypoint(addr, False) engine.analyze(self.analyze_status) self.update_model() elif key == b"F": addr = self.cur_addr() fl = self.model.AS.get_flags(addr, 0xff) if not self.require_non_func(fl): return self.show_status("Retracing as a function...") self.model.AS.make_label("fun_", addr) engine.add_entrypoint(addr, True) engine.analyze(self.analyze_status) self.update_model() self.show_status("Retraced as a function") elif key == MENU_ADD_TO_FUNC: addr = self.cur_addr() if actions.add_code_to_func(APP, addr): self.update_model() elif key == b"d": addr = self.cur_addr() fl = self.model.AS.get_flags(addr) if not self.expect_flags(fl, (self.model.AS.DATA, self.model.AS.UNK)): return if fl == self.model.AS.UNK: self.model.AS.set_flags(addr, 1, self.model.AS.DATA, self.model.AS.DATA_CONT) else: sz = self.model.AS.get_unit_size(addr) self.model.undefine_unit(addr) sz *= 2 if sz > 4: sz = 1 self.model.AS.set_flags(addr, sz, self.model.AS.DATA, self.model.AS.DATA_CONT) self.update_model() elif key == b"f": addr = self.cur_addr() fl = self.model.AS.get_flags(addr) if not self.expect_flags(fl, (self.model.AS.UNK,)): return off, area = self.model.AS.addr2area(self.cur_addr()) # Don't cross area boundaries with filler remaining = area[engine.END] - addr + 1 sz = 0 while remaining: try: fl = self.model.AS.get_flags(addr) except engine.InvalidAddrException: break if fl != self.model.AS.UNK: break b = self.model.AS.get_byte(addr) if b not in (0, 0xff): self.show_status("Filler must consist of 0x00 or 0xff") return sz += 1 addr += 1 remaining -= 1 if sz > 0: self.model.AS.make_filler(self.cur_addr(), sz) self.update_model() elif key == b"u": addr = self.cur_addr() self.model.undefine_unit(addr) self.update_model() elif key == b"h": op_no = self.cur_operand_no(self.get_cur_line()) if op_no >= 0: addr = self.cur_addr() subtype = self.model.AS.get_arg_prop(addr, op_no, "subtype") if subtype != engine.IMM_ADDR: next_subtype = { engine.IMM_UHEX: engine.IMM_UDEC, engine.IMM_UDEC: engine.IMM_UHEX, } self.model.AS.set_arg_prop(addr, op_no, "subtype", next_subtype[subtype]) self.redraw() self.show_status("Changed arg #%d to %s" % (op_no, next_subtype[subtype])) elif key == b"o": addr = self.cur_addr() line = self.get_cur_line() o = line.get_operand_addr() if not o: self.show_status("Cannot convert operand to offset") return if o.type != idaapi.o_imm or not self.model.AS.is_valid_addr(o.get_addr()): self.show_status("Cannot convert operand to offset: #%s: %s" % (o.n, o.type)) return if self.model.AS.get_arg_prop(addr, o.n, "subtype") == engine.IMM_ADDR: self.model.AS.unmake_arg_offset(addr, o.n, o.get_addr()) else: self.model.AS.make_arg_offset(addr, o.n, o.get_addr()) self.update_model(True) elif key == b";": addr = self.cur_addr() comment = self.model.AS.get_comment(addr) or "" res = DMultiEntry(60, 5, comment.split("\n"), title="Comment:").result() if res != ACTION_CANCEL: res = "\n".join(res).rstrip("\n") self.model.AS.set_comment(addr, res) self.update_model() else: self.redraw() elif key == b"n": addr = self.cur_addr() label = self.model.AS.get_label(addr) def_label = self.model.AS.get_default_label(addr) s = label or def_label while True: res = DTextEntry(30, s, title="New label:").result() if not res: break if res == def_label: res = addr else: if self.model.AS.label_exists(res): s = res self.show_status("Duplicate label") continue self.model.AS.set_label(addr, res) if not label: # If it's new label, we need to add it to model self.update_model() return break self.redraw() elif key == editor.KEY_F1: help.help(self) self.redraw() elif key == b"S": self.show_status("Saving...") saveload.save_state(project_dir) self.model.AS.changed = False self.show_status("Saved.") elif key == b"\x11": # ^Q class IssueList(WListBox): def render_line(self, l): return "%08x %s" % l d = Dialog(4, 4, title="Problems list") lw = IssueList(40, 16, self.model.AS.get_issues()) lw.finish_dialog = ACTION_OK d.add(1, 1, lw) res = d.loop() self.redraw() if res == ACTION_OK: val = lw.get_cur_line() if val: self.goto_addr(val[0], from_addr=self.cur_addr()) elif key == b"i": off, area = self.model.AS.addr2area(self.cur_addr()) props = area[engine.PROPS] percent = 100 * off / (area[engine.END] - area[engine.START] + 1) func = self.model.AS.lookup_func(self.cur_addr()) func = self.model.AS.get_label(func.start) if func else None status = "Area: 0x%x %s (%s): %.1f%%, func: %s" % ( area[engine.START], props.get("name", "noname"), props["access"], percent, func ) subarea = self.model.AS.lookup_subarea(self.cur_addr()) if subarea: status += ", subarea: " + subarea[2] self.show_status(status) elif key == b"I": from scratchabit import memmap addr = memmap.show(self.model.AS, self.cur_addr()) if addr is not None: self.goto_addr(addr, from_addr=self.cur_addr()) self.redraw() elif key == b"W": out_fname = "out.lst" with open(out_fname, "w") as f: engine.render_partial(actions.TextSaveModel(f, self), 0, 0, 10000000) self.show_status("Disassembly listing written: " + out_fname) elif key == MENU_WRITE_ALL_HTML: out_fname = "out.html" with open(out_fname, "w") as f: f.write("<pre>\n") m = actions.HTMLSaveModel(f, self) m.aspace = self.model.AS engine.render_partial(m, 0, 0, 10000000) f.write("</pre>\n") self.show_status("Disassembly HTML listing written: " + out_fname) elif key == b"\x17": # Ctrl+W outfile = actions.write_func_by_addr(APP, self.cur_addr(), feedback_obj=self) if outfile: self.show_status("Wrote file: %s" % outfile) elif key == b"\x15": # Ctrl+U # Next undefined addr = self.cur_addr() flags = self.model.AS.get_flags(addr) if flags == self.model.AS.UNK: # If already on undefined, skip the current stride of them, # as they indeed go in batches. while True: flags = self.model.AS.get_flags(addr) if flags != self.model.AS.UNK: break addr = self.model.AS.next_addr(addr) if addr is None: break if addr is not None: while True: flags = self.model.AS.get_flags(addr) if flags == self.model.AS.UNK: self.goto_addr(addr, from_addr=self.cur_addr()) break addr = self.model.AS.next_addr(addr) if addr is None: break if addr is None: self.show_status("There're no further undefined strides") elif key == b"\x06": # Ctrl+F # Next non-function addr = self.cur_addr() flags = self.model.AS.get_flags(addr, 0xff) if flags == self.model.AS.CODE: # If already on non-func code, skip the current stride of it, # as it indeed go in batches. while True: flags = self.model.AS.get_flags(addr, 0xff) self.show_status("fl=%x" % flags) if flags not in (self.model.AS.CODE, self.model.AS.CODE_CONT): break addr = self.model.AS.next_addr(addr) if addr is None: break if addr is not None: while True: flags = self.model.AS.get_flags(addr, 0xff) if flags == self.model.AS.CODE: self.goto_addr(addr, from_addr=self.cur_addr()) break addr = self.model.AS.next_addr(addr) if addr is None: break if addr is None: self.show_status("There're no further non-function code strides") elif key in (b"/", b"?"): # "/" and Shift+"/" class FoundException(Exception): pass class TextSearchModel(engine.Model): def __init__(self, substr, ctrl, this_addr, this_subno): super().__init__() self.search = substr self.ctrl = ctrl self.this_addr = this_addr self.this_subno = this_subno self.cnt = 0 def add_object(self, addr, line): super().add_object(addr, line) # Skip virtual lines before the line from which we started if addr == self.this_addr and line.subno < self.this_subno: return txt = line.render() idx = txt.find(self.search) if idx != -1: raise FoundException((addr, line.subno), idx + line.LEADER_SIZE + len(line.indent)) if self.cnt % 256 == 0: self.ctrl.show_status("Searching: 0x%x" % addr) self.cnt += 1 # Don't accumulate lines self._lines = [] self._addr2line = {} if key == b"/": d = Dialog(4, 4, title="Text Search") d.add(1, 1, WLabel("Search for:")) entry = WTextEntry(20, self.search_str) entry.finish_dialog = ACTION_OK d.add(13, 1, entry) res = d.loop() self.redraw() self.search_str = entry.get_text() if res != ACTION_OK or not self.search_str: return addr, subno = self.cur_addr_subno() else: addr, subno = self.next_line_addr_subno() try: engine.render_from(TextSearchModel(self.search_str, self, addr, subno), addr, 10000000) except FoundException as res: self.goto_addr(res.args[0], col=res.args[1], from_addr=self.cur_addr()) else: self.show_status("Not found: " + self.search_str) elif key == MENU_PREFS: uiprefs.handle(APP) elif key == MENU_PLUGIN: res = DTextEntry(30, "", title="Plugin module name:").result() self.redraw() if res: self.show_status("Running '%s' plugin..." % res) call_script(res) self.update_model() self.show_status("Plugin '%s' ran successfully" % res) else: self.show_status("Unbound key: " + repr(key))
def handle_edit_key(self, key): if key in ACTION_MAP: return ACTION_MAP[key](self) line = self.get_cur_line() if key == editor.KEY_ENTER: line = self.get_cur_line() log.info("Enter pressed: %s" % line) op_no = self.cur_operand_no(line) self.show_status("Enter pressed: %s, %s" % (self.col, op_no)) to_addr = None # No longer try to jump only to addresses in args, parse # textual representation below if False and isinstance(line, engine.DisasmObj): if op_no >= 0: o = line[op_no] to_addr = o.get_addr() if to_addr is None: o = line.get_operand_addr() if o: to_addr = o.get_addr() if to_addr is None: pos = self.col - line.LEADER_SIZE - len(line.indent) word = utils.get_word_at_pos(line.cache, pos) self.show_status("Enter pressed: %s, %s, %s" % (self.col, op_no, word)) to_addr = self.resolve_expr(word) if to_addr is None: self.show_status("Unknown address: %s" % word) return self.goto_addr(to_addr, from_addr=self.cur_addr_subno()) elif key == editor.KEY_ESC: if self.addr_stack: self.show_status("Returning") self.goto_addr(self.addr_stack.pop()) elif key == b"q": res = ACTION_OK if self.model.AS.changed: res = DConfirmation("There're unsaved changes. Quit?").result() if res == ACTION_OK: return editor.KEY_QUIT self.redraw() elif key == b"\x1b[5;5~": # Ctrl+PgUp self.goto_addr(self.model.AS.min_addr(), from_addr=line.ea) elif key == b"\x1b[6;5~": # Ctrl+PgDn self.goto_addr(self.model.AS.max_addr(), from_addr=line.ea) elif key == b"c": addr = self.cur_addr() self.show_status("Analyzing at %x" % addr) engine.add_entrypoint(addr, False) engine.analyze(self.analyze_status) self.update_model() elif key == b"F": addr = self.cur_addr() fl = self.model.AS.get_flags(addr, 0xff) if not self.require_non_func(fl): return self.show_status("Retracing as a function...") self.model.AS.make_label("fun_", addr) engine.add_entrypoint(addr, True) engine.analyze(self.analyze_status) self.update_model() self.show_status("Retraced as a function") elif key == MENU_ADD_TO_FUNC: addr = self.cur_addr() if actions.add_code_to_func(APP, addr): self.update_model() elif key == b"d": addr = self.cur_addr() fl = self.model.AS.get_flags(addr) if not self.expect_flags(fl, (self.model.AS.DATA, self.model.AS.UNK)): return if fl == self.model.AS.UNK: self.model.AS.set_flags(addr, 1, self.model.AS.DATA, self.model.AS.DATA_CONT) else: sz = self.model.AS.get_unit_size(addr) self.model.undefine_unit(addr) sz *= 2 if sz > 4: sz = 1 self.model.AS.set_flags(addr, sz, self.model.AS.DATA, self.model.AS.DATA_CONT) self.update_model() elif key == b"f": addr = self.cur_addr() fl = self.model.AS.get_flags(addr) if not self.expect_flags(fl, (self.model.AS.UNK,)): return off, area = self.model.AS.addr2area(self.cur_addr()) # Don't cross area boundaries with filler remaining = area[engine.END] - addr + 1 sz = 0 while remaining: try: fl = self.model.AS.get_flags(addr) except engine.InvalidAddrException: break if fl != self.model.AS.UNK: break b = self.model.AS.get_byte(addr) if b not in (0, 0xff): self.show_status("Filler must consist of 0x00 or 0xff") return sz += 1 addr += 1 remaining -= 1 if sz > 0: self.model.AS.make_filler(self.cur_addr(), sz) self.update_model() elif key == b"u": addr = self.cur_addr() self.model.undefine_unit(addr) self.update_model() elif key == b"h": op_no = self.cur_operand_no(self.get_cur_line()) if op_no >= 0: addr = self.cur_addr() subtype = self.model.AS.get_arg_prop(addr, op_no, "subtype") if subtype != engine.IMM_ADDR: next_subtype = { engine.IMM_UHEX: engine.IMM_UDEC, engine.IMM_UDEC: engine.IMM_UHEX, } self.model.AS.set_arg_prop(addr, op_no, "subtype", next_subtype[subtype]) self.redraw() self.show_status("Changed arg #%d to %s" % (op_no, next_subtype[subtype])) elif key == b"o": addr = self.cur_addr() line = self.get_cur_line() o = line.get_operand_addr() if not o: self.show_status("Cannot convert operand to offset") return if o.type != idaapi.o_imm or not self.model.AS.is_valid_addr(o.get_addr()): self.show_status("Cannot convert operand to offset: #%s: %s" % (o.n, o.type)) return if self.model.AS.get_arg_prop(addr, o.n, "subtype") == engine.IMM_ADDR: self.model.AS.unmake_arg_offset(addr, o.n, o.get_addr()) else: self.model.AS.make_arg_offset(addr, o.n, o.get_addr()) self.update_model(True) elif key == b";": addr = self.cur_addr() comment = self.model.AS.get_comment(addr) or "" res = DMultiEntry(60, 5, comment.split("\n"), title="Comment:").result() if res != ACTION_CANCEL: res = "\n".join(res).rstrip("\n") self.model.AS.set_comment(addr, res) self.update_model() else: self.redraw() elif key == b"n": addr = self.cur_addr() label = self.model.AS.get_label(addr) def_label = self.model.AS.get_default_label(addr) s = label or def_label while True: res = DTextEntry(30, s, title="New label:").result() if not res: break if res == def_label: res = addr else: if self.model.AS.label_exists(res): s = res self.show_status("Duplicate label") continue self.model.AS.set_label(addr, res) if not label: # If it's new label, we need to add it to model self.update_model() return break self.redraw() elif key == editor.KEY_F1: help.help(self) self.redraw() elif key == b"S": self.show_status("Saving...") saveload.save_state(project_dir) self.model.AS.changed = False self.show_status("Saved.") elif key == b"\x11": # ^Q class IssueList(WListBox): def render_line(self, l): return "%08x %s" % l d = Dialog(4, 4, title="Problems list") lw = IssueList(40, 16, self.model.AS.get_issues()) lw.finish_dialog = ACTION_OK d.add(1, 1, lw) res = d.loop() self.redraw() if res == ACTION_OK: val = lw.get_cur_line() if val: self.goto_addr(val[0], from_addr=self.cur_addr()) elif key == b"i": off, area = self.model.AS.addr2area(self.cur_addr()) props = area[engine.PROPS] percent = 100 * off / (area[engine.END] - area[engine.START] + 1) func = self.model.AS.lookup_func(self.cur_addr()) func = self.model.AS.get_label(func.start) if func else None status = "Area: 0x%x %s (%s): %.1f%%, func: %s" % ( area[engine.START], props.get("name", "noname"), props["access"], percent, func ) subarea = self.model.AS.lookup_subarea(self.cur_addr()) if subarea: status += ", subarea: " + subarea[2] self.show_status(status) elif key == b"I": from scratchabit import memmap addr = memmap.show(self.model.AS, self.cur_addr()) if addr is not None: self.goto_addr(addr, from_addr=self.cur_addr()) self.redraw() elif key == b"W": out_fname = "out.lst" with open(out_fname, "w") as f: engine.render_partial(actions.TextSaveModel(f, self), 0, 0, 10000000) self.show_status("Disassembly listing written: " + out_fname) elif key == b"\x17": # Ctrl+W outfile = actions.write_func_by_addr(APP, self.cur_addr(), feedback_obj=self) if outfile: self.show_status("Wrote file: %s" % outfile) elif key == b"\x15": # Ctrl+U # Next undefined addr = self.cur_addr() flags = self.model.AS.get_flags(addr) if flags == self.model.AS.UNK: # If already on undefined, skip the current stride of them, # as they indeed go in batches. while True: flags = self.model.AS.get_flags(addr) if flags != self.model.AS.UNK: break addr = self.model.AS.next_addr(addr) if addr is None: break if addr is not None: while True: flags = self.model.AS.get_flags(addr) if flags == self.model.AS.UNK: self.goto_addr(addr, from_addr=self.cur_addr()) break addr = self.model.AS.next_addr(addr) if addr is None: break if addr is None: self.show_status("There're no further undefined strides") elif key == b"\x06": # Ctrl+F # Next non-function addr = self.cur_addr() flags = self.model.AS.get_flags(addr, 0xff) if flags == self.model.AS.CODE: # If already on non-func code, skip the current stride of it, # as it indeed go in batches. while True: flags = self.model.AS.get_flags(addr, 0xff) self.show_status("fl=%x" % flags) if flags not in (self.model.AS.CODE, self.model.AS.CODE_CONT): break addr = self.model.AS.next_addr(addr) if addr is None: break if addr is not None: while True: flags = self.model.AS.get_flags(addr, 0xff) if flags == self.model.AS.CODE: self.goto_addr(addr, from_addr=self.cur_addr()) break addr = self.model.AS.next_addr(addr) if addr is None: break if addr is None: self.show_status("There're no further non-function code strides") elif key in (b"/", b"?"): # "/" and Shift+"/" class FoundException(Exception): pass class TextSearchModel(engine.Model): def __init__(self, substr, ctrl, this_addr, this_subno): super().__init__() self.search = substr self.ctrl = ctrl self.this_addr = this_addr self.this_subno = this_subno self.cnt = 0 def add_line(self, addr, line): super().add_line(addr, line) # Skip virtual lines before the line from which we started if addr == self.this_addr and line.subno < self.this_subno: return txt = line.render() idx = txt.find(self.search) if idx != -1: raise FoundException((addr, line.subno), idx + line.LEADER_SIZE + len(line.indent)) if self.cnt % 256 == 0: self.ctrl.show_status("Searching: 0x%x" % addr) self.cnt += 1 # Don't accumulate lines self._lines = [] self._addr2line = {} if key == b"/": d = Dialog(4, 4, title="Text Search") d.add(1, 1, WLabel("Search for:")) entry = WTextEntry(20, self.search_str) entry.finish_dialog = ACTION_OK d.add(13, 1, entry) res = d.loop() self.redraw() self.search_str = entry.get_text() if res != ACTION_OK or not self.search_str: return addr, subno = self.cur_addr_subno() else: addr, subno = self.next_line_addr_subno() try: engine.render_from(TextSearchModel(self.search_str, self, addr, subno), addr, 10000000) except FoundException as res: self.goto_addr(res.args[0], col=res.args[1], from_addr=self.cur_addr()) else: self.show_status("Not found: " + self.search_str) elif key == MENU_PREFS: uiprefs.handle(APP) elif key == MENU_PLUGIN: res = DTextEntry(30, "", title="Plugin module name:").result() self.redraw() if res: self.show_status("Running '%s' plugin..." % res) call_script(res) self.update_model() self.show_status("Plugin '%s' ran successfully" % res) else: self.show_status("Unbound key: " + repr(key))