diff --git a/Scripts/AsmViewer.py b/Scripts/AsmViewer.py index 7b78cfe..3b7e951 100644 --- a/Scripts/AsmViewer.py +++ b/Scripts/AsmViewer.py @@ -3,11 +3,14 @@ # The sample will allow you to open an assembly file and display it in color # (c) Hex-Rays # -import idaapi -import idautils -import idc + import os +import ida_idaapi +import ida_kernwin +import ida_lines +import idautils + # ---------------------------------------------------------------------- class asm_colorizer_t(object): def is_id(self, ch): @@ -79,11 +82,38 @@ class asm_colorizer_t(object): x += 1 self.add_line(s) + +class base_asmview_ah_t(ida_kernwin.action_handler_t): + def __init__(self, obj): + ida_kernwin.action_handler_t.__init__(self) + self.obj = obj + + def update(self, ctx): + if self.obj.view and self.obj.view.GetWidget() == ctx.widget: + return ida_kernwin.AST_ENABLE_FOR_WIDGET + else: + return ida_kernwin.AST_DISABLE_FOR_WIDGET + + +class refresh_ah_t(base_asmview_ah_t): + def activate(self, ctx): + self.obj.view.reload_file() + print("Reloaded") + + +class close_ah_t(base_asmview_ah_t): + def activate(self, ctx): + self.obj.view.Close() + print("Closed") + + # ----------------------------------------------------------------------- -class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t): +class asmview_t(ida_kernwin.simplecustviewer_t, asm_colorizer_t): def Create(self, fn): # Create the customview - if not idaapi.simplecustviewer_t.Create(self, "Viewing file - %s" % os.path.basename(fn)): + if not ida_kernwin.simplecustviewer_t.Create( + self, + "Viewing file - %s" % os.path.basename(fn)): return False self.instruction_list = idautils.GetInstructionList() @@ -95,9 +125,6 @@ class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t): if not self.reload_file(): return False - self.id_refresh = self.AddPopupMenu("Refresh") - self.id_close = self.AddPopupMenu("Close") - return True def reload_file(self): @@ -123,38 +150,25 @@ class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t): self.AddLine(s) def as_comment(self, s): - return idaapi.COLSTR(s, idaapi.SCOLOR_RPTCMT) + return ida_lines.COLSTR(s, ida_lines.SCOLOR_RPTCMT) def as_id(self, s): t = s.lower() if t in self.register_list: - return idaapi.COLSTR(s, idaapi.SCOLOR_REG) + return ida_lines.COLSTR(s, ida_lines.SCOLOR_REG) elif t in self.instruction_list: - return idaapi.COLSTR(s, idaapi.SCOLOR_INSN) + return ida_lines.COLSTR(s, ida_lines.SCOLOR_INSN) else: return s def as_string(self, s): - return idaapi.COLSTR(s, idaapi.SCOLOR_STRING) + return ida_lines.COLSTR(s, ida_lines.SCOLOR_STRING) def as_num(self, s): - return idaapi.COLSTR(s, idaapi.SCOLOR_NUMBER) + return ida_lines.COLSTR(s, ida_lines.SCOLOR_NUMBER) def as_directive(self, s): - return idaapi.COLSTR(s, idaapi.SCOLOR_KEYWORD) - - def OnPopupMenu(self, menu_id): - """ - A context (or popup) menu item was executed. - @param menu_id: ID previously registered with AddPopupMenu() - @return: Boolean - """ - if self.id_refresh == menu_id: - return self.reload_file() - elif self.id_close == menu_id: - self.Close() - return True - return False + return ida_lines.COLSTR(s, ida_lines.SCOLOR_KEYWORD) def OnKeydown(self, vkey, shift): """ @@ -170,8 +184,8 @@ class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t): lineno = self.GetLineNo() if lineno is not None: line, fg, bg = self.GetLine(lineno) - if line and line[0] != idaapi.SCOLOR_INV: - s = idaapi.SCOLOR_INV + line + idaapi.SCOLOR_INV + if line and line[0] != ida_lines.SCOLOR_INV: + s = ida_lines.SCOLOR_INV + line + ida_lines.SCOLOR_INV self.EditLine(lineno, s, fg, bg) self.Refresh() elif vkey == ord('C'): @@ -186,8 +200,11 @@ class asmview_t(idaapi.simplecustviewer_t, asm_colorizer_t): return True # ----------------------------------------------------------------------- -class asmviewplg(idaapi.plugin_t): - flags = idaapi.PLUGIN_KEEP +ACTNAME_REFRESH = "asmview_t::refresh" +ACTNAME_CLOSE = "asmview_t::close" + +class asmviewplg(ida_idaapi.plugin_t): + flags = ida_idaapi.PLUGIN_KEEP comment = "ASM viewer" help = "This is help" wanted_name = "ASM file viewer" @@ -196,17 +213,30 @@ class asmviewplg(idaapi.plugin_t): self.view = None def init(self): - return idaapi.PLUGIN_KEEP + # Register actions + ida_kernwin.register_action( + ida_kernwin.action_desc_t( + ACTNAME_REFRESH, "Refresh", refresh_ah_t(self))) + ida_kernwin.register_action( + ida_kernwin.action_desc_t( + ACTNAME_CLOSE, "Close", close_ah_t(self))) + return ida_idaapi.PLUGIN_KEEP + def run(self, arg): if self.view: self.Close() - fn = idaapi.ask_file(0, "*.asm", "Select ASM file to view") + fn = ida_kernwin.ask_file(0, "*.asm", "Select ASM file to view") if not fn: return self.view = asmview_t() if not self.view.Create(fn): return self.view.Show() + widget = self.view.GetWidget() + + # Attach actions to this widget's popup menu + ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_REFRESH) + ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_CLOSE) def term(self): if self.view: diff --git a/Scripts/CallStackWalk.py b/Scripts/CallStackWalk.py index cb63383..1a5f3cd 100644 --- a/Scripts/CallStackWalk.py +++ b/Scripts/CallStackWalk.py @@ -4,17 +4,19 @@ A script that tries to determine the call stack Run the application with the debugger, suspend the debugger, select a thread and finally run the script. -Copyright (c) 1990-2009 Hex-Rays +Copyright (c) 1990-2017 Hex-Rays ALL RIGHTS RESERVED. - - -v1.0 - initial version -v1.0.1 - added stack segment bitness detection, thus works with 64bit processes too """ -import idaapi -import idc +import ida_ua +import ida_bytes +import ida_kernwin +import ida_funcs +import ida_name +import ida_ida +import ida_idp +import ida_segment +import ida_dbg import idautils -from ida_kernwin import Choose # ----------------------------------------------------------------------- # class to take a copy of a segment_t @@ -24,6 +26,7 @@ class Seg(): self.end_ea = s.end_ea self.perm = s.perm self.bitness = s.bitness + def __cmp__(self, other): return cmp(self.start_ea, other.start_ea) @@ -51,18 +54,18 @@ def IsPrevInsnCall(ea): is a CALL instruction """ global CallPattern - if ea == idaapi.BADADDR or ea < 10: + if ea == ida_idaapi.BADADDR or ea < 10: return None for delta, opcodes in CallPattern: # assume caller's ea caller = ea + delta # get the bytes - bytes = [x for x in GetDataList(caller, len(opcodes), 1)] + bytes = [x for x in idautils.GetDataList(caller, len(opcodes), 1)] # do we have a match? is it a call instruction? if bytes == opcodes: - tmp = idaapi.insn_t() - if idaapi.decode_insn(tmp, caller) and idaapi.is_call_insn(tmp): + insn = ida_ua.insn_t() + if ida_ua.decode_insn(insn, caller) and ida_idp.is_call_insn(insn): return caller return None @@ -79,32 +82,35 @@ def CallStackWalk(nn): def __init__(self, caller, sp): self.caller = caller self.sp = sp - f = idaapi.get_func(caller) - self.displ = "" + f = ida_funcs.get_func(caller) + self.displ = "%08x: " % caller if f: - self.displ += idc.get_func_name(caller) + self.displ += ida_funcs.get_func_name(caller) t = caller - f.start_ea if t > 0: self.displ += "+" + hex(t) else: self.displ += hex(caller) self.displ += " [" + hex(sp) + "]" + def __str__(self): + return self.displ + # get stack pointer - sp = cpu.Esp - seg = idaapi.getseg(sp) + sp = idautils.cpu.Esp + seg = ida_segment.getseg(sp) if not seg: return (False, "Could not locate stack segment!") stack_seg = Seg(seg) word_size = 2 ** (seg.bitness + 1) callers = [] - sp = cpu.Esp - word_size + sp = idautils.cpu.Esp - word_size while sp < stack_seg.end_ea: sp += word_size ptr = idautils.GetDataList(sp, 1, word_size).next() - seg = idaapi.getseg(ptr) + seg = ida_segment.getseg(ptr) # only accept executable segments - if (not seg) or ((seg.perm & idaapi.SEGPERM_EXEC) == 0): + if (not seg) or ((seg.perm & ida_segment.SEGPERM_EXEC) == 0): continue # try to find caller caller = IsPrevInsnCall(ptr) @@ -118,16 +124,16 @@ def CallStackWalk(nn): if ret: ea = ret[0] # function exists? - f = idaapi.get_func(ea) + f = ida_funcs.get_func(ea) if not f: # create function - idc.add_func(ea, idaapi.BADADDR) + ida_funcs.add_func(ea) # get the flags - f = idc.get_flags(caller) + f = ida_bytes.get_flags(caller) # no code there? - if not is_code(f): - create_insn(caller) + if not ida_bytes.is_code(f): + ida_ua.create_insn(caller) callers.append(Result(caller, sp)) # @@ -135,52 +141,46 @@ def CallStackWalk(nn): # ----------------------------------------------------------------------- # Chooser class -class CallStackWalkChoose(Choose): +class CallStackWalkChoose(ida_kernwin.Choose): def __init__(self, title, items): - Choose.__init__(self, title, [ ["Caller", 16], ["Display", 250] ]) + ida_kernwin.Choose.__init__( + self, + title, + [["Location", 30]]) self.items = items - - def OnGetLine(self, n): - o = self.items[n] - line = [] - line.append("%X" % o.caller) - line.append("%s" % o.displ) - return line + self.modal = True def OnGetSize(self): return len(self.items) + def OnGetLine(self, n): + return [str(self.items[n])] + def OnSelectLine(self, n): - o = self.items[n] - jumpto(o.caller) - return (Choose.NOTHING_CHANGED, ) + ida_kernwin.jumpto(self.items[n].caller) # ----------------------------------------------------------------------- def main(): - if not idaapi.is_debugger_on(): - idc.warning("Please run the process first!") + if not ida_dbg.is_debugger_on(): + ida_kernwin.warning("Please run the process first!") return - if idaapi.get_process_state() != -1: - idc.warning("Please suspend the debugger first!") + if ida_dbg.get_process_state() != -1: + ida_kernwin.warning("Please suspend the debugger first!") return - # only avail from IdaPython r232 - if hasattr(idaapi, "NearestName"): - # get all debug names - dn = idaapi.get_debug_names(idaapi.cvar.inf.min_ea, idaapi.cvar.inf.max_ea) - # initiate a nearest name search (using debug names) - nn = idaapi.NearestName(dn) - else: - nn = None + # get all debug namesp + dn = ida_name.get_debug_names(ida_ida.cvar.inf.min_ea, ida_ida.cvar.inf.max_ea) + # initiate a nearest name search (using debug names) + nn = ida_name.NearestName(dn) ret, callstack = CallStackWalk(nn) if ret: - title = "Call stack walker (thread %X)" % (get_current_thread()) - idaapi.close_chooser(title) + title = "Call stack walker (thread %X)" % (ida_dbg.get_current_thread()) + ida_kernwin.close_chooser(title) c = CallStackWalkChoose(title, callstack) - c.Show() + c.Show(True) else: - idc.warning("Failed to walk the stack:" + callstack) + ida_kernwin.warning("Failed to walk the stack:" + callstack) # ----------------------------------------------------------------------- main() diff --git a/Scripts/DbgCmd.py b/Scripts/DbgCmd.py index d19e564..e3e8e2d 100644 --- a/Scripts/DbgCmd.py +++ b/Scripts/DbgCmd.py @@ -2,37 +2,70 @@ # Debugger command prompt with CustomViewers # (c) Hex-Rays # -import idaapi -import idc -from idaapi import simplecustviewer_t +import ida_idaapi +import ida_kernwin +import ida_lines +import ida_expr +import ida_dbg -def SendDbgCommand(cmd): - """Sends a command to the debugger and returns the output string. - An exception will be raised if the debugger is not running or the current debugger does not export - the 'send_dbg_command' IDC command. - """ - s = idc.eval('send_dbg_command("%s");' % cmd) - if s.startswith("IDC_FAILURE"): - raise Exception, "Debugger command is available only when the debugger is active!" - return s +# The viewer instance +dbgcmd = None # ----------------------------------------------------------------------- -class dbgcmd_t(simplecustviewer_t): +class base_dbgcmd_ah_t(ida_kernwin.action_handler_t): + def __init__(self): + ida_kernwin.action_handler_t.__init__(self) + + def update(self, ctx): + if dbgcmd and ctx.widget == dbgcmd.GetWidget(): + return ida_kernwin.AST_ENABLE_FOR_WIDGET + else: + return ida_kernwin.AST_DISABLE_FOR_WIDGET + +# ----------------------------------------------------------------------- +class clear_dbgcmd_ah_t(base_dbgcmd_ah_t): + def activate(self, ctx): + dbgcmd.ResetOutput() + +# ----------------------------------------------------------------------- +class newcmd_dbgcmd_ah_t(base_dbgcmd_ah_t): + def activate(self, ctx): + dbgcmd.IssueCommand() + +# ----------------------------------------------------------------------- +class close_dbgcmd_ah_t(base_dbgcmd_ah_t): + def activate(self, ctx): + dbgcmd.Close() + +# ----------------------------------------------------------------------- +# Register actions (if needed) +ACTNAME_CLEAR = "dbgcmd:clear" +ACTNAME_NEWCMD = "dbgcmd:newcmd" +ACTNAME_CLOSE = "dbgcmd:close" +ida_kernwin.register_action( + ida_kernwin.action_desc_t( + ACTNAME_CLEAR, "Clear", clear_dbgcmd_ah_t(), "x")) +ida_kernwin.register_action( + ida_kernwin.action_desc_t( + ACTNAME_NEWCMD, "New command", newcmd_dbgcmd_ah_t(), "Insert")) +ida_kernwin.register_action( + ida_kernwin.action_desc_t( + ACTNAME_CLOSE, "Close", close_dbgcmd_ah_t(), "Escape")) + +# ----------------------------------------------------------------------- +class dbgcmd_t(ida_kernwin.simplecustviewer_t): def Create(self): # Form the title title = "Debugger command window" # Create the customview - if not simplecustviewer_t.Create(self, title): + if not ida_kernwin.simplecustviewer_t.Create(self, title): return False self.last_cmd = "" - self.menu_clear = self.AddPopupMenu("Clear") - self.menu_cmd = self.AddPopupMenu("New command") - self.ResetOutput() return True def IssueCommand(self): - s = idaapi.ask_str(self.last_cmd, 0, "Please enter a debugger command") + s = ida_kernwin.ask_str(self.last_cmd, 0, "Please enter a debugger command") if not s: return @@ -40,43 +73,24 @@ class dbgcmd_t(simplecustviewer_t): self.last_cmd = s # Add it using a different color - self.AddLine("debugger>" + idaapi.COLSTR(s, idaapi.SCOLOR_VOIDOP)) + self.AddLine("debugger>" + ida_lines.COLSTR(s, ida_lines.SCOLOR_VOIDOP)) - try: - r = SendDbgCommand(s).split("\n") - for s in r: - self.AddLine(idaapi.COLSTR(s, idaapi.SCOLOR_LIBNAME)) - except: - self.AddLine(idaapi.COLSTR("Debugger is not active or does not export send_dbg_command()", idaapi.SCOLOR_ERROR)) + ok, out = ida_dbg.send_dbg_command(s) + if ok: + for line in out.split("\n"): + self.AddLine(ida_lines.COLSTR(line, ida_lines.SCOLOR_LIBNAME)) + else: + self.AddLine( + ida_lines.COLSTR( + "Debugger is not active or does not export ida_dbg.send_dbg_command() (%s)" % out, + ida_lines.SCOLOR_ERROR)) self.Refresh() def ResetOutput(self): self.ClearLines() - self.AddLine(idaapi.COLSTR("Please press INS to enter command; X to clear output", idaapi.SCOLOR_AUTOCMT)) + self.AddLine(ida_lines.COLSTR("Please press INS to enter command; X to clear output", ida_lines.SCOLOR_AUTOCMT)) self.Refresh() - def OnKeydown(self, vkey, shift): - # ESCAPE? - if vkey == 27: - self.Close() - # VK_INSERT - elif vkey == 45: - self.IssueCommand() - elif vkey == ord('X'): - self.ResetOutput() - else: - return False - return True - - def OnPopupMenu(self, menu_id): - if menu_id == self.menu_clear: - self.ResetOutput() - elif menu_id == self.menu_cmd: - self.IssueCommand() - else: - # Unhandled - return False - return True # ----------------------------------------------------------------------- def show_win(): @@ -85,17 +99,16 @@ def show_win(): print "Failed to create debugger command line!" return None x.Show() + + # Attach actions to this widget's popup menu + widget = x.GetWidget() + ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_CLEAR) + ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_NEWCMD) + ida_kernwin.attach_action_to_popup(widget, None, ACTNAME_CLOSE) return x -try: - # created already? - dbgcmd +if dbgcmd is not None: dbgcmd.Close() - del dbgcmd -except: - pass + dbgcmd = None dbgcmd = show_win() -if not dbgcmd: - del dbgcmd - diff --git a/Scripts/DrvsDispatch.py b/Scripts/DrvsDispatch.py index ae7c298..defbb74 100644 --- a/Scripts/DrvsDispatch.py +++ b/Scripts/DrvsDispatch.py @@ -2,30 +2,34 @@ A script to demonstrate how to send commands to the debugger and then parse and use the output in IDA -Copyright (c) 1990-2009 Hex-Rays +Copyright (c) 1990-2017 Hex-Rays ALL RIGHTS RESERVED. """ import re -import idc -from ida_kernwin import Choose + +import ida_idaapi +import ida_expr +import ida_kernwin +import ida_dbg + +# ----------------------------------------------------------------------- +def WinDbg_command(cmd): + ok, s = ida_dbg.send_dbg_command(cmd) + return s if ok else False # ----------------------------------------------------------------------- def CmdDriverList(): - s = idc.eval('send_dbg_command("lm o");') - if "IDC_FAILURE" in s: return False - return s + return WinDbg_command("lm o") # ----------------------------------------------------------------------- def CmdDrvObj(drvname, flag=2): - return idc.eval('send_dbg_command("!drvobj %s %d");' % (drvname, flag)) + return WinDbg_command("!drvobj %s %d" % (drvname, flag)) # ----------------------------------------------------------------------- def CmdReloadForce(): - s = idc.eval('send_dbg_command(".reload /f");') - if "IDC_FAILURE" in s: return False - return True + return WinDbg_command(".reload /f") # ----------------------------------------------------------------------- # class to hold dispatch entry information @@ -33,82 +37,83 @@ class DispatchEntry: def __init__(self, addr, name): self.addr = addr self.name = name + def __repr__(self): + return "%08X: %s" % (self.addr, self.name) # ----------------------------------------------------------------------- def GetDriverDispatch(): - # return a list of arrays of the form: [addr, name] - ret_list = [] - - # build the RE for parsing output from the "lm o" command - re_drv = re.compile('^[a-f0-9]+\s+[a-f0-9]+\s+(\S+)', re.I) - # build the RE for parsing output from the "!drvobj DRV_NAME 2" command - re_tbl = re.compile('^\[\d{2}\]\s+IRP_MJ_(\S+)\s+([0-9a-f]+)', re.I) + # return a list of arrays of the form: [addr, name] + ret_list = [] - # force reloading of module symbols - if not CmdReloadForce(): - print "Could not communicate with WinDbg, make sure the debugger is running!" - return None + # build the RE for parsing output from the "lm o" command + re_drv = re.compile('^[a-f0-9]+\s+[a-f0-9]+\s+(\S+)', re.I) - # get driver list - lm_out = CmdDriverList() - if not lm_out: - return "Failed to get driver list!" + # build the RE for parsing output from the "!drvobj DRV_NAME 2" command + re_tbl = re.compile('^\[\d{2}\]\s+IRP_MJ_(\S+)\s+([0-9a-f]+)', re.I) - # for each line - for line in lm_out.split("\n"): - # parse - r = re_drv.match(line) - if not r: continue + # force reloading of module symbols + if not CmdReloadForce(): + print "Could not communicate with WinDbg, make sure the debugger is running!" + return None - # extract driver name - drvname = r.group(1).strip() - - # execute "drvobj" command - tbl_out = CmdDrvObj(drvname) - - if not tbl_out: - print "Failed to get driver object for", drvname - continue + # get driver list + lm_out = CmdDriverList() + if not lm_out: + return "Failed to get driver list!" # for each line - for line in tbl_out.split("\n"): + for line in lm_out.split("\n"): # parse - r = re_tbl.match(line) + r = re_drv.match(line) if not r: continue - disp_addr = int(r.group(2), 16) # convert hex string to number - disp_name = "Dispatch" + r.group(1) - ret_list.append(DispatchEntry(disp_addr, drvname + "_" + disp_name)) - return ret_list + # extract driver name + drvname = r.group(1).strip() + + # execute "drvobj" command + tbl_out = CmdDrvObj(drvname) + + if not tbl_out: + print "Failed to get driver object for", drvname + continue + + # for each line + for line in tbl_out.split("\n"): + # parse + r = re_tbl.match(line) + if not r: continue + disp_addr = int(r.group(2), 16) # convert hex string to number + disp_name = "Dispatch" + r.group(1) + ret_list.append(DispatchEntry(disp_addr, drvname + "_" + disp_name)) + + return ret_list # ----------------------------------------------------------------------- # Chooser class -class DispatchChoose(Choose): +class DispatchChoose(ida_kernwin.Choose): def __init__(self, title, items): - Choose.__init__(self, title, [ ["Address", 16], ["Name", 250] ]) + ida_kernwin.Choose.__init__( + self, + title, + [["Address", 30]], + width=250) self.items = items - def OnGetLine(self, n): - o = self.items[n] - line = [] - line.append("%08X" % o.addr) - line.append("%s" % o.name) - return line - def OnGetSize(self): return len(self.items) + def OnGetLine(self, n): + return [str(self.items[n])] + def OnSelectLine(self, n): - o = self.items[n] - Jump(o.addr) - return (Choose.NOTHING_CHANGED, ) + ida_kernwin.jumpto(self.items[n].addr) # ----------------------------------------------------------------------- # main r = GetDriverDispatch() if r: c = DispatchChoose("Dispatch table browser", r) - c.Show() + c.Show(True) else: - print "Failed to retrieve dispatchers list!" + print "Failed to retrieve dispatchers list!" diff --git a/Scripts/ExchainDump.py b/Scripts/ExchainDump.py index 75c729d..bb29f28 100644 --- a/Scripts/ExchainDump.py +++ b/Scripts/ExchainDump.py @@ -7,56 +7,48 @@ ALL RIGHTS RESERVED. """ -import idc import re + import ida_kernwin -from ida_kernwin import Choose # class to store parsed results class exchain: def __init__(self, m): - self.name = m.group(1) - self.addr = int(m.group(2), 16) + self.name = m.group(1) + self.addr = int(m.group(2), 16) # Chooser class -class MyChoose(Choose): +class MyChoose(ida_kernwin.Choose): def __init__(self, title, items): - Choose.__init__(self, title, [ ["Address", 16], ["Name", 250] ]) + ida_kernwin.Choose.__init__(self, title, [ ["Address", 16], ["Name", 250] ]) self.items = items def OnGetLine(self, n): o = self.items[n] - line = [] - line.append("%08X" % o.addr) - line.append("%s" % o.name) - return line + return ["%08X" % o.addr, o.name] def OnGetSize(self): return len(self.items) def OnSelectLine(self, n): - o = self.items[n] - Jump(o.addr) - return (Choose.NOTHING_CHANGED, ) + ida_kernwin.jumpto(self.items[n].addr) + return (ida_kernwin.Choose.NOTHING_CHANGED, ) -# main def main(): - s = idc.eval('send_dbg_command("!exchain")') - if "IDC_FAILURE" in s: - return (False, "Cannot execute the command") + ok, s = ida_dbg.send_dbg_command("!exchain") + if not ok: + return (False, "Cannot execute the command (%s)" % s) matches = re.finditer(r'[^:]+: ([^\(]+) \(([^\)]+)\)\n', s) - L = [] - for x in matches: - L.append(exchain(x)) - if not L: + entries = [exchain(x) for x in matches] + if not entries: return (False, "Nothing to display: Could parse the result!") - # Get a Choose instance - chooser = MyChoose("Exchain choose", L) - # Run the chooser + # Show a list of results, and let the user possibly jump to one of those + chooser = MyChoose("Exchain choose", entries) chooser.Show() return (True, "Success!") + ok, r = main() if not ok: print r diff --git a/Scripts/FindInstructions.py b/Scripts/FindInstructions.py index d85d67c..8c50017 100644 --- a/Scripts/FindInstructions.py +++ b/Scripts/FindInstructions.py @@ -1,5 +1,5 @@ """ -FindInstructions.py: A script to help you find desired opcodes/instructions in a database +A script to help you find desired opcodes/instructions in a database The script accepts opcodes and assembly statements (which will be assembled) separated by semicolon @@ -13,15 +13,22 @@ The general syntax is: * To specify in which context the instructions should be assembled, pass asm_where=ea: find("jmp dword ptr [esp]", asm_where=here()) -Copyright (c) 1990-2009 Hex-Rays +Copyright (c) 1990-2017 Hex-Rays ALL RIGHTS RESERVED. - -v1.0 - initial version """ -import idaapi +import re + +import ida_idaapi +import ida_lines +import ida_segment +import ida_kernwin +import ida_bytes +import ida_ua +import ida_ida +import ida_search +import ida_funcs + import idautils -import idc -from ida_kernwin import Choose # ----------------------------------------------------------------------- def FindInstructions(instr, asm_where=None): @@ -31,8 +38,9 @@ def FindInstructions(instr, asm_where=None): """ if not asm_where: # get first segment - asm_where = get_first_seg() - if asm_where == idaapi.BADADDR: + seg = ida_segment.get_first_seg() + asm_where = seg.start_ea if seg else ida_idaapi.BADADDR + if asm_where == ida_idaapi.BADADDR: return (False, "No segments defined") # regular expression to distinguish between opcodes and instructions @@ -49,7 +57,7 @@ def FindInstructions(instr, asm_where=None): buf = ''.join([chr(int(x, 16)) for x in line.split()]) else: # assemble the instruction - ret, buf = Assemble(asm_where, line) + ret, buf = idautils.Assemble(asm_where, line) if not ret: return (False, "Failed to assemble:"+line) # add the assembled buffer @@ -66,63 +74,66 @@ def FindInstructions(instr, asm_where=None): # find all binary strings print "Searching for: [%s]" % bin_str - ea = get_inf_attr(INF_MIN_EA) + ea = ida_ida.cvar.inf.min_ea ret = [] while True: - ea = find_binary(ea, SEARCH_DOWN, bin_str) - if ea == idaapi.BADADDR: + ea = ida_search.find_binary(ea, ida_idaapi.BADADDR, bin_str, 16, ida_search.SEARCH_DOWN) + if ea == ida_idaapi.BADADDR: break ret.append(ea) - msg(".") + ida_kernwin.msg(".") ea += tlen if not ret: return (False, "Could not match [%s]" % bin_str) - msg("\n") + ida_kernwin.msg("\n") return (True, ret) # ----------------------------------------------------------------------- # Chooser class -class SearchResultChoose(Choose): +class SearchResultChoose(ida_kernwin.Choose): def __init__(self, title, items): - Choose.__init__(self, title, [ ["Address", 16], ["Results", 250] ]) + ida_kernwin.Choose.__init__( + self, + title, + [["Address", 30], ["Function (or segment)", 25], ["Instruction", 20]], + width=250) self.items = items - def OnGetLine(self, n): - o = self.items[n] - line = [] - line.append("%08X" % o.ea) - line.append("%s" % o.display) - return line - def OnGetSize(self): return len(self.items) + def OnGetLine(self, n): + i = self.items[n] + ea = i.ea + return [ + hex(i.ea), + i.funcname_or_segname, + i.text + ] + def OnSelectLine(self, n): - o = self.items[n] - Jump(o.ea) - return (Choose.NOTHING_CHANGED, ) + ida_kernwin.jumpto(self.items[n].ea) # ----------------------------------------------------------------------- # class to represent the results class SearchResult: def __init__(self, ea): self.ea = ea - if not is_code(get_flags(ea)): - create_insn(ea) - t = idaapi.generate_disasm_line(ea) + self.funcname_or_segname = "" + self.text = "" + if not ida_bytes.is_code(ida_bytes.get_flags(ea)): + ida_ua.create_insn(ea) + + # text + t = ida_lines.generate_disasm_line(ea) if t: - line = idaapi.tag_remove(t) - else: - line = "" - func = get_func_name(ea) - self.display = "" - if func: - self.display += func + ": " - else: - n = get_segm_name(ea) - if n: - self.display += n + ": " - self.display += line + self.text = ida_lines.tag_remove(t) + + # funcname_or_segname + n = ida_funcs.get_func_name(ea) \ + or ida_segment.get_segm_name(ida_segment.getseg(ea)) + if n: + self.funcname_or_segname = n # ----------------------------------------------------------------------- def find(s=None, x=False, asm_where=None): @@ -132,16 +143,16 @@ def find(s=None, x=False, asm_where=None): if x: results = [] for ea in ret: - seg = idaapi.getseg(ea) - if (not seg) or (seg.perm & idaapi.SEGPERM_EXEC) == 0: + seg = ida_segment.getseg(ea) + if (not seg) or (seg.perm & ida_segment.SEGPERM_EXEC) == 0: continue results.append(SearchResult(ea)) else: results = [SearchResult(ea) for ea in ret] title = "Search result for: [%s]" % s - idaapi.close_chooser(title) + ida_kernwin.close_chooser(title) c = SearchResultChoose(title, results) - c.Show() + c.Show(True) else: print ret diff --git a/Scripts/ImpRef.py b/Scripts/ImpRef.py index 9d9471c..fe6827a 100644 --- a/Scripts/ImpRef.py +++ b/Scripts/ImpRef.py @@ -4,12 +4,14 @@ # # (c) Hex-Rays # - -import idaapi -import idc -import idautils import re +import ida_kernwin +import ida_nalt +import ida_funcs + +import idautils + # ----------------------------------------------------------------------- def find_imported_funcs(dllname): def imp_cb(ea, name, ord): @@ -19,12 +21,12 @@ def find_imported_funcs(dllname): return True imports = [] - nimps = idaapi.get_import_module_qty() + nimps = ida_nalt.get_import_module_qty() for i in xrange(0, nimps): - name = idaapi.get_import_module_name(i) + name = ida_nalt.get_import_module_name(i) if re.match(dllname, name, re.IGNORECASE) is None: continue - idaapi.enum_import_names(i, imp_cb) + ida_nalt.enum_import_names(i, imp_cb) return imports @@ -38,9 +40,9 @@ def find_import_ref(dllname): for xref in idautils.XrefsTo(ea): # check if referrer is a thunk ea = xref.frm - f = idaapi.get_func(ea) - if f and (f.flags & idaapi.FUNC_THUNK) != 0: - imports.append([f.start_ea, idaapi.get_func_name(f.start_ea), 0]) + f = ida_funcs.get_func(ea) + if f and (f.flags & ida_funcs.FUNC_THUNK) != 0: + imports.append([f.start_ea, ida_funcs.get_func_name(f.start_ea), 0]) #print "\t%x %s: from a thunk, parent added %x" % (ea, name, f.start_ea) continue @@ -54,7 +56,7 @@ def find_import_ref(dllname): # ----------------------------------------------------------------------- def main(): - dllname = idaapi.ask_str('kernel32', 0, "Enter module name") + dllname = ida_kernwin.ask_str('kernel32', 0, "Enter module name") if not dllname: print("Cancelled") return diff --git a/Scripts/PteDump.py b/Scripts/PteDump.py index 705e7c0..37a168d 100644 --- a/Scripts/PteDump.py +++ b/Scripts/PteDump.py @@ -1,8 +1,9 @@ -import idaapi -import idc -from ida_kernwin import Choose -def parse_pte(str): +import ida_kernwin +import ida_segment +import ida_dbg + +def parse_pte(s): try: parse_pte.re except: @@ -10,15 +11,17 @@ def parse_pte(str): parse_pte.items = ('pde', 'pte', 'pdec', 'ptec', 'pdepfn', 'pdepfns', 'ptepfn', 'ptepfns') m = parse_pte.re.search(s) + if not m: + return None r = {} for i in range(0, len(parse_pte.items)): r[parse_pte.items[i]] = m.group(i+1) return r -class MyChoose(Choose): +class MyChoose(ida_kernwin.Choose): def __init__(self, title, ea1, ea2): - Choose.__init__(self, title, [ ["VA", 10], ["PTE attr", 30] ]) + ida_kernwin.Choose.__init__(self, title, [ ["VA", 10], ["PTE attr", 30] ]) self.ea1 = ea1 self.ea2 = ea2 self.icon = 5 @@ -26,17 +29,14 @@ class MyChoose(Choose): self.Refresh() def OnGetLine(self, n): - print("getline %d" % n) return self.items[n] def OnGetSize(self): - n = len(self.items) - self.Refresh() - return n + return len(self.items) def OnRefresh(self, n): - print("refresh %d" % n) - return None # call standard refresh + self.Refresh() + return (ida_kernwin.Choose.ALL_CHANGED, ) def Refresh(self): items = [] @@ -44,11 +44,12 @@ class MyChoose(Choose): ea1 = self.ea1 npages = (self.ea2 - ea1) / PG for i in range(npages): - r = idc.send_dbg_command("!pte %x" % ea1) - if not r: + ok, r = ida_dbg.send_dbg_command("!pte %x" % ea1) + if not ok: return False r = parse_pte(r) - items.append([hex(ea1), r['ptepfns']]) + if r: + items.append([hex(ea1), r['ptepfns']]) ea1 += PG self.items = items @@ -66,16 +67,17 @@ def DumpPTE(ea1, ea2): PG = 0x1000 npages = (ea2 - ea1) / PG for i in range(npages): - r = idc.send_dbg_command("!pte %x" % ea1) - if not r: + ok, r = ida_dbg.send_dbg_command("!pte %x" % ea1) + if not ok: return False - print r r = parse_pte(r) - print("VA: %08X PTE: %s PDE: %s" % (ea1, r['ptepfns'], r['pdepfns'])) + if r: + print("VA: %08X PTE: %s PDE: %s" % (ea1, r['ptepfns'], r['pdepfns'])) ea1 += PG def DumpSegPTE(ea): - DumpPTE(idc.get_segm_start(ea), idc.get_segm_end(ea)) + s = ida_segment.getseg(ea) + DumpPTE(s.start_ea, s.end_ea) DumpSegPTE(here()) diff --git a/Scripts/SEHGraph.py b/Scripts/SEHGraph.py index 6ad3a0c..61d72f8 100644 --- a/Scripts/SEHGraph.py +++ b/Scripts/SEHGraph.py @@ -4,26 +4,24 @@ A script that graphs all the exception handlers in a given process It will be easy to see what thread uses what handler and what handlers are commonly used between threads -Copyright (c) 1990-2009 Hex-Rays +Copyright (c) 1990-2017 Hex-Rays ALL RIGHTS RESERVED. - - -v1.0 - initial version - """ -import idaapi -import idautils -import idc +import ida_kernwin +import ida_graph +import ida_idd +import ida_dbg +import ida_funcs -from idaapi import GraphViewer +import idautils # ----------------------------------------------------------------------- # Since Windbg debug module does not support get_thread_sreg_base() # we will call the debugger engine "dg" command and parse its output def WindbgGetRegBase(tid): - s = idc.eval('send_dbg_command("dg %x")' % cpu.fs) - if "IDC_FAILURE" in s: + ok, s = ida_dbg.send_dbg_command("dg %x" % idautils.cpu.fs) + if not ok: return 0 m = re.compile("[0-9a-f]{4} ([0-9a-f]{8})") t = m.match(s.split('\n')[-2]) @@ -33,8 +31,8 @@ def WindbgGetRegBase(tid): # ----------------------------------------------------------------------- def GetFsBase(tid): - idc.select_thread(tid) - base = idaapi.dbg_get_thread_sreg_base(tid, cpu.fs) + ida_dbg.select_thread(tid) + base = ida_idd.dbg_get_thread_sreg_base(tid, idautils.cpu.fs) if base != 0: return base return WindbgGetRegBase(tid) @@ -43,7 +41,8 @@ def GetFsBase(tid): # Walks the SEH chain and returns a list of handlers def GetExceptionChain(tid): fs_base = GetFsBase(tid) - exc_rr = get_wide_dword(fs_base) + print("FS_BASE for %s: %s (cpu.fs=%s)" % (repr(tid), repr(fs_base), repr(idautils.cpu.fs))) + exc_rr = ida_bytes.get_wide_dword(fs_base) result = [] while exc_rr != 0xffffffff: prev = get_wide_dword(exc_rr) @@ -53,9 +52,9 @@ def GetExceptionChain(tid): return result # ----------------------------------------------------------------------- -class SEHGraph(GraphViewer): +class SEHGraph(ida_graph.GraphViewer): def __init__(self, title, result): - GraphViewer.__init__(self, title) + ida_graph.GraphViewer.__init__(self, title) self.result = result self.names = {} # ea -> name @@ -74,13 +73,13 @@ class SEHGraph(GraphViewer): # Add each handler for handler in chain: # Check if a function is created at the handler's address - f = idaapi.get_func(handler) + f = ida_funcs.get_func(handler) if not f: # create function - idc.add_func(handler, idaapi.BADADDR) + ida_funcs.add_func(handler) # Node label is function name or address - s = get_func_name(handler) + s = ida_funcs.get_func_name(handler) if not s: s = "%x" % handler @@ -110,7 +109,7 @@ class SEHGraph(GraphViewer): def OnDblClick(self, node_id): is_thread, value, label = self[node_id] if is_thread: - idc.select_thread(value) + ida_dbg.select_thread(value) self.Show() s = "SEH chain for " + hex(value) t = "-" * len(s) @@ -121,18 +120,18 @@ class SEHGraph(GraphViewer): print "%x: %s" % (handler, self.names[handler]) print t else: - idc.jumpto(value) + ida_kernwin.jumpto(value) return True # ----------------------------------------------------------------------- def main(): - if not idaapi.dbg_can_query(): + if not ida_idd.dbg_can_query(): print "The debugger must be active and suspended before using this script!" return # Save current thread id - tid = get_current_thread() + tid = ida_dbg.get_current_thread() # Iterate through all function instructions and take only call instructions result = {} @@ -140,7 +139,7 @@ def main(): result[tid] = GetExceptionChain(tid) # Restore previously selected thread - idc.select_thread(tid) + ida_dbg.select_thread(tid) # Build the graph g = SEHGraph("SEH graph", result) diff --git a/Scripts/VaDump.py b/Scripts/VaDump.py index bcb3d33..82eed1a 100644 --- a/Scripts/VaDump.py +++ b/Scripts/VaDump.py @@ -7,11 +7,10 @@ ALL RIGHTS RESERVED. """ -import idc -from ida_kernwin import Choose - import re +import ida_kernwin + # class to store parsed results class memva: def __init__(self, m): @@ -29,7 +28,7 @@ class memva: self.typestr = "" # Chooser class -class MemChoose(Choose): +class MemChoose(ida_kernwin.Choose): def __init__(self, title, items): headers = [] headers.append(["Base", 10]) @@ -37,7 +36,7 @@ class MemChoose(Choose): headers.append(["State", 20]) headers.append(["Protect", 20]) headers.append(["Type", 20]) - Choose.__init__(self, title, headers) + ida_kernwin.Choose.__init__(self, title, headers) self.items = items def OnGetLine(self, n): @@ -55,13 +54,13 @@ class MemChoose(Choose): def OnSelectLine(self, n): o = self.items[n] - idc.jumpto(o.base) - return (NOTHING_CHANGED, ) + ida_kernwin.jumpto(o.base) + return (ida_kernwin.Choose.NOTHING_CHANGED, ) # main def main(): - s = idc.eval('send_dbg_command("!vadump")') - if "IDC_FAILURE" in s: + ok, s = ida_dbg.send_dbg_command("!vadump") + if not ok: return (False, "Cannot execute the command") matches = re.finditer(r'BaseAddress:\s*?(\w+?)\n' \ diff --git a/Scripts/msdnapihelp.py b/Scripts/msdnapihelp.py index b920c6e..72881f9 100644 --- a/Scripts/msdnapihelp.py +++ b/Scripts/msdnapihelp.py @@ -5,30 +5,33 @@ This script fetches the API reference (from MSDN) of a given highlighted identif and returns the results in a new web browser page. This script depends on the feedparser package: http://code.google.com/p/feedparser/ - -10/05/2010 -- initial version - - """ -import idaapi +# ----------------------------------------------------------------------- +import ida_kernwin +import ida_name +import ida_idaapi + +try: + import feedparser +except: + ida_kernwin.warning('Feedparser package not installed') # ----------------------------------------------------------------------- -class msdnapihelp_plugin_t(idaapi.plugin_t): - flags = idaapi.PLUGIN_UNL +class msdnapihelp_plugin_t(ida_idaapi.plugin_t): + flags = ida_idaapi.PLUGIN_UNL comment = "Online MSDN API Help" help = "Help me" wanted_name = "MSDN API Help" wanted_hotkey = "F3" def init(self): - return idaapi.PLUGIN_OK + return ida_idaapi.PLUGIN_OK @staticmethod def sanitize_name(name): - t = idaapi.FUNC_IMPORT_PREFIX + t = ida_name.FUNC_IMPORT_PREFIX if name.startswith(t): return name[len(t):] return name @@ -36,28 +39,25 @@ class msdnapihelp_plugin_t(idaapi.plugin_t): def run(self, arg): # Get the highlighted identifier - v = idaapi.get_current_viewer() - id = ida_kernwin.get_highlight(v)[0] - if not id: + v = ida_kernwin.get_current_viewer() + ident, ok = ida_kernwin.get_highlight(v) + if not ok: print "No identifier was highlighted" return - import webbrowser - - try: - import feedparser - except: - idaapi.warning('Feedparser package not installed') - return - - id = self.sanitize_name(id) - print "Looking up '%s' in MSDN online" % id - d = feedparser.parse("http://social.msdn.microsoft.com/Search/Feed.aspx?locale=en-us&format=RSS&Query=%s" % id) + ident = self.sanitize_name(ident) + print "Looking up '%s' in MSDN online" % ident + qurl = "https://social.msdn.microsoft.com/search/en-US/feed?query=%s&format=RSS&theme=feed%%2fen-us" + d = feedparser.parse(qurl % ident) if len(d['entries']) > 0: url = d['entries'][0].link - webbrowser.open_new_tab(url) + if arg > 0: + print("URL: %s" % url) + else: + import webbrowser + webbrowser.open_new_tab(url) else: - print "API documentation not found for: %s" % id + print "API documentation not found for: %s" % ident def term(self): diff --git a/out_of_tree/parsed_notifications.zip b/out_of_tree/parsed_notifications.zip index 2cec574..cdeb21a 100644 Binary files a/out_of_tree/parsed_notifications.zip and b/out_of_tree/parsed_notifications.zip differ diff --git a/python/idc.py b/python/idc.py index 6d3183f..24f5838 100644 --- a/python/idc.py +++ b/python/idc.py @@ -2611,6 +2611,7 @@ AF_FINAL = 0x80000000 # Final pass of analysis INF_AF2 = 44 # uint32; Analysis flags 2 AF2_DOEH = 0x00000001 # Handle EH information +AF2_DORTTI = 0x00000002 # Handle RTTI information INF_BASEADDR = 48 # uval_t; base paragraph of the program INF_START_SS = 52 # int32; value of SS at the start @@ -8338,6 +8339,13 @@ if sys.modules["__main__"].IDAPYTHON_COMPAT_695_API: def MakeStr(ea, endea): return create_strlit(ea, endea) + def GetProcessorName(): + return ida_ida.cvar.inf.procname + + def SegStart(ea): return get_segm_start(ea) + def SegEnd(ea): return get_segm_end(ea) + def SetSegmentType(ea, type): return set_segm_type(ea, type) + # Convenience functions: def here(): return get_screen_ea() def is_mapped(ea): return (prev_addr(ea+1)==ea) diff --git a/pywraps/py_dbg.py b/pywraps/py_dbg.py index 87b22ef..04b8909 100644 --- a/pywraps/py_dbg.py +++ b/pywraps/py_dbg.py @@ -1,5 +1,7 @@ # +import ida_idaapi import ida_idd +import ida_expr def get_tev_reg_val(tev, reg): rv = ida_idd.regval_t() @@ -27,6 +29,32 @@ def get_tev_reg_mem_ea(tev, idx): if get_insn_tev_reg_mem(tev, mis): if idx >= 0 and idx < mis.size(): return mis[idx].ea + +def send_dbg_command(command): + """ + Send a direct command to the debugger backend, and + retrieve the result as a string. + + Note: any double-quotes in 'command' must be backslash-escaped. + Note: this only works with some debugger backends: Bochs, WinDbg, GDB. + + Returns: (True, ) on success, or (False, ) on failure + """ + rv = ida_expr.idc_value_t() + err = ida_expr.eval_idc_expr(rv, ida_idaapi.BADADDR, """send_dbg_command("%s");""" % command) + if err: + return False, "eval_idc_expr() failed: %s" % err + vtype = ord(rv.vtype) + if vtype == ida_expr.VT_STR: + s = rv.c_str() + if "IDC_FAILURE" in s: + return False, "eval_idc_expr() reported an error: %s" % s + return True, s + elif vtype == ida_expr.VT_LONG: + return True, str(rv.num) + else: + return False, "eval_idc_expr(): wrong return type: %d" % vtype + # # diff --git a/pywraps/py_idd.hpp b/pywraps/py_idd.hpp index f426871..52c97f5 100644 --- a/pywraps/py_idd.hpp +++ b/pywraps/py_idd.hpp @@ -236,11 +236,15 @@ static PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_va { PYW_GIL_CHECK_LOCKED_SCOPE(); - if ( !dbg_can_query() || !PyInt_Check(py_tid) || !PyInt_Check(py_sreg_value) ) + if ( !dbg_can_query() + || (!PyInt_Check(py_tid) && !PyLong_Check(py_tid)) + || (!PyInt_Check(py_sreg_value) && !PyLong_Check(py_sreg_value)) ) + { Py_RETURN_NONE; + } ea_t answer; - thid_t tid = PyInt_AsLong(py_tid); - int sreg_value = PyInt_AsLong(py_sreg_value); + thid_t tid = PyLong_AsLong(py_tid); + int sreg_value = PyLong_AsLong(py_sreg_value); if ( internal_get_sreg_base(&answer, tid, sreg_value) != 1 ) Py_RETURN_NONE; diff --git a/pywraps/py_idd.py b/pywraps/py_idd.py index 45ca764..3a008ad 100644 --- a/pywraps/py_idd.py +++ b/pywraps/py_idd.py @@ -224,11 +224,14 @@ class Appcall_callable__(object): class Appcall_consts__(object): """Helper class used by Appcall.Consts attribute It is used to retrieve constants via attribute access""" - def __init__(self, default=0): + def __init__(self, default=None): self.__default = default def __getattr__(self, attr): - return Appcall__.valueof(attr, self.__default) + v = Appcall__.valueof(attr, self.__default) + if v is None: + raise ValueError, "No constant with name " + attr + return v # ----------------------------------------------------------------------- class Appcall__(object): diff --git a/pywraps/py_kernwin.py b/pywraps/py_kernwin.py index d426796..0a5b77b 100644 --- a/pywraps/py_kernwin.py +++ b/pywraps/py_kernwin.py @@ -91,7 +91,10 @@ chtype_segreg=chtype_srcp close_tform=close_widget find_tform=find_widget get_current_tform=get_current_widget -get_highlighted_identifier=get_highlight +def get_highlighted_identifier(): + thing = get_highlight(get_current_widget()) + if thing and thing[1]: + return thing[0] get_tform_title=get_widget_title get_tform_type=get_widget_type is_chooser_tform=is_chooser_widget diff --git a/swig/diskio.i b/swig/diskio.i index 070e564..fea5496 100644 --- a/swig/diskio.i +++ b/swig/diskio.i @@ -67,6 +67,21 @@ %ignore close_linput; %rename (close_linput) py_close_linput; +%apply qstrvec_t *out { qstrvec_t *dirs }; + +%cstring_output_buf_and_size_returning_charptr( + 1, + char *buf, + size_t bufsize, + const char *filename, + const char *subdir); // getsysfile +%cstring_output_buf_and_size_returning_charptr( + 3, + linput_t *li, + int64 fpos, + char *buf, + size_t bufsize); // qlgetz + %include "diskio.hpp" %{ diff --git a/swig/expr.i b/swig/expr.i index c5291fd..02d2caa 100644 --- a/swig/expr.i +++ b/swig/expr.i @@ -63,6 +63,12 @@ %ignore compile_idc_text; %rename (compile_idc_text) py_compile_idc_text; +%cstring_output_buf_and_size_returning_charptr( + 1, + char *buf, + size_t bufsize, + const char *file); // get_idc_filename + %nonnul_argument_prototype( bool py_compile_idc_file(const char *nonnul_line, qstring *errbuf), const char *nonnul_line); diff --git a/swig/fixup.i b/swig/fixup.i index 009569c..6395e59 100644 --- a/swig/fixup.i +++ b/swig/fixup.i @@ -7,4 +7,15 @@ %ignore register_custom_fixup; %ignore unregister_custom_fixup; +%cstring_output_qstring_returning_charptr( + 1, + qstring *buf, + ea_t source); // fixup_data_t::get_desc + +%cstring_output_qstring_returning_charptr( + 1, + qstring *buf, + ea_t source, + const fixup_data_t &fd); // get_fixup_desc + %include "fixup.hpp" diff --git a/swig/idp.i b/swig/idp.i index 6f95b45..ee69655 100644 --- a/swig/idp.i +++ b/swig/idp.i @@ -80,6 +80,20 @@ static PyObject *AssembleLine(ea_t ea, ea_t cs, ea_t ip, bool use32, const char *nonnul_line), const char *nonnul_line); +#ifndef SWIGIMPORTED // let's not modify the wrappers for modules %import'ing us (e.g., typeinf.i, hexrays.i) +%cstring_output_buf_and_size_returning_charptr( + 1, + char *buf, + size_t bufsize); // get_idp_name +#endif // SWIGIMPORTED + +%cstring_output_qstring_returning_charptr( + 1, + qstring *out, + const char *name, + uint32 disable_mask, + int demreq); // ev_demangle_name + %include "idp.hpp" %include "config.hpp" diff --git a/swig/kernwin.i b/swig/kernwin.i index 22ffe02..669376d 100644 --- a/swig/kernwin.i +++ b/swig/kernwin.i @@ -131,6 +131,9 @@ extern plugin_t PLUGIN; %ignore place_t__serialize; %ignore place_t::deserialize; %ignore place_t__deserialize; +%ignore place_t::generate; +%ignore place_t__generate; +%rename (generate) py_generate; %ignore register_place_class; %ignore register_loc_converter; @@ -297,6 +300,21 @@ static void _py_unregister_compiled_form(PyObject *py_form, bool shutdown); static enumplace_t *as_enumplace_t(place_t *p) { return (enumplace_t *) p; } static structplace_t *as_structplace_t(place_t *p) { return (structplace_t *) p; } static simpleline_place_t *as_simpleline_place_t(place_t *p) { return (simpleline_place_t *) p; } + + PyObject *py_generate(void *ud, int maxsize) + { + qstrvec_t lines; + int deflnnum = 0; + color_t pfx_color = 0; + bgcolor_t bgcolor = DEFCOLOR; + int generated = $self->generate(&lines, &deflnnum, &pfx_color, &bgcolor, ud, maxsize); + PyObject *tuple = PyTuple_New(4); + PyTuple_SetItem(tuple, 0, qstrvec2pylist(lines)); + PyTuple_SetItem(tuple, 1, PyLong_FromLong(deflnnum)); + PyTuple_SetItem(tuple, 2, PyLong_FromLong(uchar(pfx_color))); + PyTuple_SetItem(tuple, 3, PyLong_FromLong(bgcolor)); + return tuple; + } } %extend twinpos_t { diff --git a/swig/typeinf.i b/swig/typeinf.i index 58dc76f..009fa6e 100644 --- a/swig/typeinf.i +++ b/swig/typeinf.i @@ -255,6 +255,12 @@ %ignore remove_tinfo_pointer; %rename (remove_tinfo_pointer) py_remove_tinfo_pointer; +%cstring_output_buf_and_size_returning_charptr( + 2, + ea_t ea, + char *buf, + size_t bufsize); // idc_guess_type, idc_get_type + %include "typeinf.hpp" // Custom wrappers diff --git a/tools/deploy/header.i.in b/tools/deploy/header.i.in index 720cef4..165923a 100644 --- a/tools/deploy/header.i.in +++ b/tools/deploy/header.i.in @@ -468,6 +468,28 @@ static PyObject *type##_get_clink_ptr(PyObject *self) } %enddef +//------------------------------------------------------------------------- +// and a helper for overriding the 'argout' of some of those functions, +// that return the input char* as return code; those build on recent +// versions of Xcode (>= 9.0), and need special care +%define %cstring_output_buf_and_size_returning_charptr(BUFIDX, ...) +%typemap(argout) (__VA_ARGS__) { + // cstring_output_buf_and_size_returning_charptr's argout + Py_XDECREF(resultobj); + if (result != NULL) + { + resultobj = PyString_FromString($BUFIDX); + } + else + { + Py_INCREF(Py_None); + resultobj = Py_None; + } + qfree($BUFIDX); +} +%enddef + + //--------------------------------------------------------------------- // IN/OUT qstring //--------------------------------------------------------------------- @@ -491,8 +513,43 @@ static PyObject *type##_get_clink_ptr(PyObject *self) // Nothing. We certainly don't want 'temp' to be deleted. } -//--------------------------------------------------------------------- -// md5/sha256 hash retrieval +//------------------------------------------------------------------------- +// see %cstring_output_buf_and_size_returning_charptr above +%define %cstring_output_qstring_returning_charptr(BUFIDX, ...) +%typemap(argout) (__VA_ARGS__) { + // cstring_output_qstring_returning_charptr's argout + Py_XDECREF(resultobj); + if (result != NULL) + { + resultobj = PyString_FromStringAndSize($BUFIDX->begin(), $BUFIDX->length()); + } + else + { + Py_INCREF(Py_None); + resultobj = Py_None; + } +} +%enddef + +//------------------------------------------------------------------------- +// OUT qstrvec_t +//------------------------------------------------------------------------- +%typemap(in,numinputs=0) qstrvec_t *out (qstrvec_t temp) { + $1 = &temp; +} +%typemap(argout) qstrvec_t *out +{ + Py_XDECREF(resultobj); + resultobj = qstrvec2pylist(*($1)); +} +%typemap(freearg) qstrvec_t* out +{ + // Nothing. We certainly don't want 'temp' to be deleted. +} + +//------------------------------------------------------------------------- +// md5/sha256 hash retrieval +//------------------------------------------------------------------------- %typemap(in, numinputs=0) uchar hash[ANY] (uchar temp[$1_dim0]) { $1 = temp; @@ -771,7 +828,7 @@ struct wrapped_array_t { %{ -static PyObject *qstrvec2pylist(qstrvec_t &vec) +static PyObject *qstrvec2pylist(const qstrvec_t &vec) { size_t n = vec.size(); PyObject *py_list = PyList_New(n); diff --git a/tools/gen_idc_bc695.py b/tools/gen_idc_bc695.py index 23c12a9..01afe72 100644 --- a/tools/gen_idc_bc695.py +++ b/tools/gen_idc_bc695.py @@ -64,6 +64,10 @@ forbidden = [ "SaveBase", "eval", "MakeStr", + "GetProcessorName", + "SegStart", + "SegEnd", + "SetSegmentType", ] symbols_modules = {