def run(self, arg): if not idaapi.get_root_filename(): rs_log('please load a file/idb before') return global SyncForm if not SyncForm: SyncForm = SyncForm_t() SyncForm.Show() rs_log("plugin loaded")
def cb_broker_finished(self): rs_log("broker finished") self.uninit_hotkeys() if self.broker: self.broker.worker.stop() self.cb_sync.stateChanged.disconnect(self.cb_change_state) self.cb_sync.toggle() self.cb_sync.stateChanged.connect(self.cb_change_state) self.btn.setText("Start")
def cb_change_state(self, state): if state == QtCore.Qt.Checked: rs_log("sync enabled") # Restart broker self.hotkeys_ctx = [] self.init_broker() else: if self.broker: self.smooth_kill() rs_log("sync disabled\n")
def translate_notice(self): if not self.dbg_dialect: rs_log("idb isn't synced yet, can't translate") return ea = idaapi.get_screen_ea() mod = self.name.split('.')[0].strip() cmd = self.dbg_dialect['prefix'] + "translate 0x%x 0x%x %s" % (self.base, ea, mod) self.notice_broker("cmd", "\"cmd\":\"%s\"" % cmd) rs_debug("translate address 0x%x" % ea)
def pdb_name_warning(self, name): pdbpath = DbgDirHlpr.read_rsds_pdb() if not pdbpath: return normpath = os.path.normpath(pdbpath.replace("\\", "\\\\")) pdb_root, pdb_ext = os.path.splitext(os.path.basename(normpath)) mod_root, mod_ext = os.path.splitext(name) if pdb_root.strip() != mod_root.strip(): rs_log("hint: pdb name ('%s') differs from registered module name ('%s')" % (pdb_root+mod_ext, name))
def hbp_notice(self, oneshot=False): if not self.is_active: rs_log("idb isn't enabled, hbp can't be set") return ea = idaapi.get_screen_ea() offset = self.rebase_remote(ea) cmd = "%s0x%x" % (self.dbg_dialect['hbp1' if oneshot else 'hbp'], offset) self.notice_broker("cmd", "\"cmd\":\"%s\"" % cmd) rs_log(">> set %s" % cmd)
def init_single_hotkey(self, key, fnCb, conflict=None): # 'mute' existing action shortcut if present if conflict: ida_kernwin.update_action_shortcut(conflict, None) ctx = idaapi.add_hotkey(key, fnCb) if ctx is None: rs_log("failed to register hotkey %s" % key) del ctx else: self.hotkeys_ctx.append((ctx, key, conflict))
def append_cmt(self, ea, cmt, rptble=False): if len(cmt) > 1024: rs_log("warning, comment needs to be splitted (from 0x%x)" % ea) nh = idaapi.next_head(ea, ida_idaapi.BADADDR) if nh == ida_idaapi.BADADDR: rs_log('[x] failed to find next instruction candidate') return self.append_cmt(nh, cmt[1024:], rptble) cmt = cmt[:1024] idaapi.append_cmt(ea, cmt, rptble)
def req_rcmt(self, hash): msg, offset, base = hash['msg'], hash['offset'], hash['base'] offset, msg = self.addr_switch(offset, msg) if not offset: return ea = self.rebase(base, offset) if not ea: return idaapi.set_cmt(ea, str(''), False) rs_log("reset comment at 0x%x" % ea)
def req_cmt(self, hash): msg, offset, base = hash['msg'], hash['offset'], hash['base'] offset, msg = self.addr_switch(offset, msg) if not offset: return ea = self.rebase(base, offset) if not ea: return self.append_cmt(ea, str(msg)) rs_log("comment added at 0x%x" % ea)
def handle_name_aliasing(self): name = idaapi.get_root_filename() rs_log("default idb name: %s" % name) # check in conf for name aliasing os.environ['IDB_PATH'] = os.path.realpath(IDB_PATH) for loc in ('IDB_PATH', 'USERPROFILE', 'HOME'): if loc in os.environ: try: confpath = os.path.join(os.path.realpath(os.environ[loc]), '.sync') if os.path.exists(confpath): rs_log("found config file: %s" % confpath) config = ConfigParser() config.read(confpath) if config.has_option('ALIASES', name): alias = config.get('ALIASES', name) if alias != "": name = alias rs_log("overwrite idb name with %s" % name) break except Exception as e: rs_log('failed to load configuration file') self.pdb_name_warning(name) return name
def export_bp_notice(self): if not self.dbg_dialect: rs_log("idb isn't synced yet, can't export bp") return mod = self.name.split('.')[0].strip() nbp = ida_dbg.get_bpt_qty() for i in range(nbp): ea = idc.get_bpt_ea(i) attrs = [idc.BPTATTR_TYPE, idc.BPTATTR_COND, idc.BPTATTR_FLAGS] btype, cond, flags = [idc.get_bpt_attr(ea, x) for x in attrs] if cond: rs_log("bp %d: conditional bp not supported" % i) else: if ((btype in [idc.BPT_EXEC, idc.BPT_SOFT]) and ((flags & idc.BPT_ENABLED) != 0)): offset = ea - self.base bp = self.dbg_dialect['hbp' if (btype == idc.BPT_EXEC) else 'bp'] cmd = "%s%s+0x%x" % (bp, mod, offset) self.notice_broker("cmd", "\"cmd\":\"%s\"" % cmd) rs_log("bp %d: %s" % (i, cmd)) rs_log('export done')
def uninit_hotkeys(self): if not self.hotkeys_ctx: return # delete registered context and restore original action for ctx, key, conflict in self.hotkeys_ctx: if idaapi.del_hotkey(ctx): del ctx else: rs_log("failed to delete hotkey %s" % key) if conflict: ida_kernwin.update_action_shortcut(conflict, key) self.hotkeys_ctx = []
def init_single_hotkey(self, key, fnCb, conflict=None): if conflict: if self.cmd_hooks.minver74sp1(): # 'hook' existing action shortcut when possible self.cmd_hooks.add_hook(conflict, fnCb) return else: # 'mute' existing action shortcut ida_kernwin.update_action_shortcut(conflict, None) ctx = idaapi.add_hotkey(key, fnCb) if ctx is None: rs_log("failed to register hotkey %s" % key) del ctx else: self.hotkeys_ctx.append((ctx, key, conflict))
def req_lbl(self, hash): msg, offset, base = hash['msg'], hash['offset'], hash['base'] offset, msg = self.addr_switch(offset, msg) if not offset: return ea = self.rebase(base, offset) if not ea: return flags = False if str(msg).startswith('@@'): flags = idaapi.SN_LOCAL idaapi.set_name(ea, str(msg), flags) rs_log("label added at 0x%x" % ea)
def bp_notice(self, oneshot=False): if not self.is_active: rs_log("idb isn't enabled, bp can't be set") return ea = idaapi.get_screen_ea() offset = self.rebase_remote(ea) if offset is not None: cmd = "%s0x%x" % (self.dbg_dialect['bp1' if oneshot else 'bp'], offset) if (oneshot and 'oneshot_post' in self.dbg_dialect): cmd += self.dbg_dialect['oneshot_post'] self.notice_broker("cmd", "\"cmd\":\"%s\"" % cmd) rs_log(">> set %s" % cmd)
def req_raddr(self, hash): raddr, rbase, offset, base = hash['raddr'], hash['rbase'], hash['offset'], hash['base'] ea = self.rebase(base, offset) if not ea: return if self.base_remote != rbase: rs_log('could not rebase this address, 0x%x != 0x0, not in module') return addr = self.rebase(rbase, raddr) if not addr: return self.append_cmt(ea, "0x%x (rebased from 0x%x)" % (addr, raddr)) rs_log("comment added at 0x%x" % ea)
def req_broker(self, hash): subtype = hash['subtype'] if (subtype == 'msg'): # simple message announcement rs_log("<< broker << %s" % hash['msg']) elif (subtype == 'notice'): # notice from broker self.broker_port = int(hash['port']) rs_debug("<< broker << binding on port %d" % self.broker_port) for attempt in range(rsconfig.CONNECT_BROKER_MAX_ATTEMPT): try: host = socket.gethostbyname('localhost') self.broker_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.broker_sock.settimeout(2) self.broker_sock.connect((host, self.broker_port)) break except socket.error: rs_log('failed to connect to broker') rs_log(sys.exc_info()) if self.broker_sock: self.broker_sock.close() self.broker_sock = None time.sleep(0.1) if (attempt == (rsconfig.CONNECT_BROKER_MAX_ATTEMPT - 1)): self.announcement( "[sync] failed to connect to broker (attempt %d)" % attempt) raise RuntimeError # request broker to validate its beacon time.sleep(0.4) self.beacon_notice() # enable/disable idb, if disable it drops most sync requests elif (subtype == 'enable_idb'): self.is_active = True rs_log('idb is enabled') elif (subtype == 'disable_idb'): self.is_active = False self.base_remote = None self.cb_restore_last_line() rs_log('idb is disabled')
def req_fcmt(self, hash): msg, offset, base = hash['msg'], hash['offset'], hash['base'] offset, msg = self.addr_switch(offset, msg) if not offset: return ea = self.rebase(base, offset) if not ea: return func = idaapi.get_func(ea) if not func: rs_log("could not find func for 0x%x" % ea) return idaapi.set_func_cmt(func, str(msg), False) rs_log("function comment added at 0x%x" % ea)
def rebase(self, base, offset): if base is not None: # check for non-compliant debugger client if base > offset: rs_log('unsafe addr: 0x%x > 0x%x' % (base, offset)) return None # update base address of remote module if self.base_remote != base: self.base_remote = base offset = self.rebase_local(offset) if not self.is_safe(offset): rs_log('unsafe addr: 0x%x not in valid segment' % (offset)) return None return offset
def uninit_hotkeys(self): # disable ida_kernwin.UI_Hooks if self.cmd_hooks.minver74sp1(): self.cmd_hooks.unhook() if not self.hotkeys_ctx: return # delete registered context and restore original action for ctx, key, conflict in self.hotkeys_ctx: if idaapi.del_hotkey(ctx): del ctx else: rs_log("failed to delete hotkey %s" % key) if conflict and not self.cmd_hooks.minver74sp1(): ida_kernwin.update_action_shortcut(conflict, key) self.hotkeys_ctx = []
def export_bp_notice(self): if not self.dbg_dialect: rs_log("idb isn't synced yet, can't export bp") return is_windbg = (self.dbg_dialect == 'windbg') # Windbg supports relative address, ie. mod+0xCAFE # for non relative address the remote base address is needed if (not is_windbg) and (not self.base_remote): rs_log("idb isn't enabled, can't export bp") return mod = self.name.split('.')[0].strip() nbp = ida_dbg.get_bpt_qty() for i in range(nbp): ea = idc.get_bpt_ea(i) attrs = [idc.BPTATTR_TYPE, idc.BPTATTR_COND, idc.BPTATTR_FLAGS] btype, cond, flags = [idc.get_bpt_attr(ea, x) for x in attrs] if cond: rs_log("bp %d: conditional bp not supported" % i) else: if ((btype in [idc.BPT_EXEC, idc.BPT_SOFT]) and ((flags & idc.BPT_ENABLED) != 0)): bp = self.dbg_dialect['hbp' if (btype == idc.BPT_EXEC) else 'bp'] if is_windbg: offset = ea - self.base cmd = "%s%s+0x%x" % (bp, mod, offset) else: offset = self.rebase_remote(ea) cmd = "%s0x%x" % (bp, offset) self.notice_broker("cmd", "\"cmd\":\"%s\"" % cmd) rs_log("bp %d: %s" % (i, cmd)) rs_log('export done')
def addr_switch(self, offset, msg): if (not msg) or (msg == ''): return [offset, msg] try: args = self.parser.parse_args(msg.split()) except argparse.ArgumentError: rs_log('failed to parse command') return [None, msg] # no address switch supplied if not args.address: return [offset, msg] try: addr = int(''.join(args.address), 16) except (TypeError, ValueError): rs_log('failed to parse address, should be hex') return [None, msg] # make sure the address points to a valid instruction/data head = idaapi.get_item_head(addr) if head != addr: rs_log("ambiguous address, did you mean 0x%x ?" % head) return [None, msg] return [addr, ' '.join(args.msg)]
def init_broker(self): rs_debug("init_broker") modname = self.input.text() if modname == "": modname = self.handle_name_aliasing() self.input.setText(modname) cmdline = "\"%s\" -u \"%s\" --idb \"%s\"" % (PYTHON_PATH, BROKER_PATH, modname) rs_log("cmdline: %s" % cmdline) try: self.broker = Broker(self.parser) self.broker.started.connect(self.cb_broker_started) self.broker.finished.connect(self.cb_broker_finished) self.broker.start(cmdline) except Exception as e: rs_log("[-] failed to start broker: %s\n%s" % (str(e), traceback.format_exc())) return self.broker.worker.name = modname
def handle_name_aliasing(self): name = idaapi.get_root_filename() rs_log("default idb name: %s" % name) try: conf = load_configuration(name) if conf.path: rs_log("found config file: %s" % repr(conf)) if conf.alias: name = conf.alias rs_log("overwrite idb name with %s" % name) except Exception as e: rs_log('failed to load configuration file') self.pdb_name_warning(name) return name
def init_broker(self): rs_debug("init_broker") modname = self.input.text() cmdline = "\"%s\" -u \"%s\" --idb \"%s\"" % (PYTHON_PATH, BROKER_PATH, modname) rs_log("cmdline: %s" % cmdline) self.broker = Broker(self.parser) env = QProcessEnvironment.systemEnvironment() env.insert("IDB_PATH", IDB_PATH) env.insert("PYTHON_PATH", PYTHON_PATH) try: self.broker.started.connect(self.cb_broker_started) self.broker.finished.connect(self.cb_broker_finished) self.broker.setProcessEnvironment(env) self.broker.start(cmdline) except Exception as e: rs_log("[-] failed to start broker: %s\n%s" % (str(e), traceback.format_exc())) return self.init_hotkeys() self.broker.worker.name = modname
def read_rsds_codeview(): guid = None penode = idaapi.netnode() penode.create(idautils.peutils_t.PE_NODE) fpos = penode.altval(idautils.peutils_t.PE_ALT_DBG_FPOS) if (fpos == 0): rs_log('No debug directory') return guid input_file = ida_nalt.get_input_file_path() if not os.path.exists(input_file): rs_log('input file not available') else: with open(input_file, 'rb') as fd: fd.seek(fpos) raw = fd.read(0x1C) """ typedef struct _IMAGE_DEBUG_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Type; DWORD SizeOfData; DWORD AddressOfRawData; DWORD PointerToRawData; } IMAGE_DEBUG_DIRECTORY, *PIMAGE_DEBUG_DIRECTORY; """ dbgdir = struct.unpack('LLHHLLLL', raw) # 2, IMAGE_DEBUG_TYPE_CODEVIEW if not (dbgdir[4] == 2): rs_log('not CODEVIEW data') else: fd.seek(dbgdir[7]) if not (fd.read(4).decode('ascii') == 'RSDS'): rs_log("unsupported CODEVIEW information format (%s)" % sig) else: d1, d2, d3 = struct.unpack('LHH', fd.read(0x8)) d4 = struct.unpack('>H', fd.read(0x2))[0] d5 = binascii.hexlify(fd.read(0x6)).upper() guid = "%08X-%04X-%04X-%04X-%s" % (d1, d2, d3, d4, d5) return guid
def req_rrln(self, hash): sym, rbase, offset, base = hash['sym'], hash['rbase'], hash['offset'], hash['base'] rs_log("%s - 0x%x - 0x%x - 0x%x" % (sym, rbase, offset, base)) addr = idc.get_name_ea_simple(sym) if addr: self.notice_broker("cmd", "\"cmd\":\"%s\"" % addr) rs_log("resolved address: %s" % addr) else: rs_log("could not resolve address for symbol %s" % sym)
def req_rrln(self, hash): sym = hash['sym'] rs_log("rrln> symbol \"%s\"" % sym) addr = idc.get_name_ea_simple(str(sym)) if addr: raddr = self.rebase_remote(addr) self.notice_broker("cmd", "\"cmd\":\"%s\"" % raddr) rs_log("rrln> remote: 0x%x, local: 0x%x)" % (raddr, addr)) else: rs_log("rrln> symbol not found \"%s\"" % sym)
def req_bps_set(self, hash): blob = hash['msg'] rs_log('[-] save .bpcmds') node = idaapi.netnode(rsconfig.NETNODE_INDEX) if not node: rs_log('[-] failed to open netnode store') self.notice_broker('cmd', "\"cmd\":\" -> failed to save .bpcmds") return new = node.create(rsconfig.NETNODE_STORE) if new == 0: rs_log(' -> creating new netnode store') out = node.setblob(rs_encode(blob), 0, chr(1)) self.notice_broker("cmd", "\"cmd\":\" -> .bpcmds saved\"") return