diff --git a/CHANGES.txt b/CHANGES.txt
index b6fdd9e..1f26313 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,20 +1,40 @@
Please see http://code.google.com/p/idapython/source/list for a detailed list of changes.
+
+Changes from version 1.4.3 to 1.5.0
+------------------------------------
+- IDA Pro 6.1 support
+- Added AskUsingForm() with embedded forms support (check ex_askusingform.py example and formchooser.py in the SDK)
+- Added idautils.DecodePreviousInstruction() / DecodePrecedingInstruction()
+- Added idc.BeginTypeUpdating() / EndTypeUpdating() for fast batch type update operations
+- Added more IDP callbacks
+- Added UI_Hooks with a few notification events
+- Added idaapi.process_ui_action() / idc.ProcessUiAction()
+- Added netnode.index() to get netnode number
+- Better handling of ea_t values with bitwise negation
+- Execute statement hotkey (Ctrl-F3), script timeout, and other options are now configurable with Python.cfg
+- bugfix: idaapi.msg() / error() and warning() so they don't accept vararg
+- bugfix: processor_t.id constants were incorrect
+- bugfix: get_debug_names() was broken with IDA64
+- Various bugfixes
+
Changes from version 1.4.2 to 1.4.3
------------------------------------
-- IDA 6.0 support
+- IDA Pro 6.0 support
- Python CLI now prints expression evaluation result (no need to use print())
- Changed Alt-8 to Ctrl-F3 (because it conflicts with window switching key Alt+n)
- Added get_highlighted_identifier()
- Added PluginForm class to allow UI development with either PyQt4 or PySide
- Added idautils.Entries() to enum entrypoints
+
Changes from version 1.4.1 to 1.4.2
------------------------------------
- Added command completion
- Added necessary changes so it compiles with Python 2.7
- Wrapped set_user_defined_prefix()
+
Changes from version 1.4.0 to 1.4.1
------------------------------------
- Added cli_t
@@ -22,6 +42,7 @@ Changes from version 1.4.0 to 1.4.1
- Changed the copyright string to IDAPython Team
- Some platform dependant classes are present but useable only where applicable
+
Changes from version 1.3.0 to 1.4.0
------------------------------------
- IDA Pro 5.7 support
@@ -35,9 +56,9 @@ Changes from version 1.3.0 to 1.4.0
- Documented all manually wrapped functions (check 'pywraps' module in the docs)
- Lots of cleanups and fixes
+
Changes from version 1.2.0 to 1.3.0
------------------------------------
-
- IDA Pro 5.6 support
- Added Appcall mechanism
- Added procregs to idautils.py (r254)
@@ -45,7 +66,6 @@ Changes from version 1.2.0 to 1.3.0
Changes from version 1.1.0 to 1.2.0
------------------------------------
-
- 64-bit support (largely untested)
- IDA Pro 5.5 support
- Long running (or inifinitely looping) scripts can now be stopped
@@ -58,7 +78,6 @@ Changes from version 1.1.0 to 1.2.0
Changes from version 0.9.0 to 1.0.0
-----------------------------------
-
- Upgraded IDA Pro base version to 5.1
- Dropped Python 2.4 support
- Mac OS X support
@@ -76,7 +95,6 @@ Changes from version 0.9.0 to 1.0.0
Changes from version 0.8.0 to 0.9.0
-----------------------------------
-
- Upgraded base version to IDA Pro 5.0
- Works with IDA Pro 5.1
- Python 2.4 and 2.5 supported
@@ -92,7 +110,6 @@ Changes from version 0.8.0 to 0.9.0
Changes from version 0.7.0 to 0.8.0
-----------------------------------
-
- Added support for IDA Pro 4.9
- Dropped support for IDA Pro 4.7
- NOTE: Windows version is linked against Python 2.4.
diff --git a/README.txt b/README.txt
index 7acf4c2..f90bab5 100644
--- a/README.txt
+++ b/README.txt
@@ -38,10 +38,10 @@ Mailing list for the project is hosted by Google Groups at
INSTALLATION FROM BINARIES
--------------------------
-1. Install Python 2.5 or 2.6 from http://www.python.org/
-2. Copy the python and python64 directories to the IDA install directory
-3. Copy the plugins to the %IDADIR%\plugins\
-
+1. Install 2.6 or 2.7 from http://www.python.org/
+2. Copy "python" directory to %IDADIR%
+3. Copy "plugins" directory to the %IDADIR%\plugins\
+4. Copy "python.cfg" to %IDADIR%\cfg
USAGE
-----
@@ -88,3 +88,4 @@ or
The user init file is read and executed at the end of the init process.
+Please note that IDAPython can be configured with "python.cfg" file.
diff --git a/Scripts/AsmViewer.py b/Scripts/AsmViewer.py
new file mode 100644
index 0000000..ae70add
--- /dev/null
+++ b/Scripts/AsmViewer.py
@@ -0,0 +1,216 @@
+# -----------------------------------------------------------------------
+# This is an example illustrating how to use customview in Python
+# 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
+
+# ----------------------------------------------------------------------
+class asm_colorizer_t(object):
+ def is_id(self, ch):
+ return ch == '_' or ch.isalpha() or '0' <= ch <= '9'
+
+ def get_identifier(self, line, x, e):
+ i = x
+ is_digit = line[i].isdigit()
+ while i < e:
+ ch = line[i]
+ if not self.is_id(ch):
+ if ch != '.' or not is_digit:
+ break
+ i += 1
+ return (i, line[x:i])
+
+ def get_quoted_string(self, line, x, e):
+ quote = line[x]
+ i = x + 1
+ while i < e:
+ ch = line[i]
+ if ch == '\\' and line[i+1] == quote:
+ i += 1
+ elif ch == quote:
+ i += 1 # also take the quote
+ break
+ i += 1
+ return (i, line[x:i])
+
+ def colorize(self, lines):
+ for line in lines:
+ line = line.rstrip()
+ if not line:
+ self.add_line()
+ continue
+ x = 0
+ e = len(line)
+ s = ""
+ while x < e:
+ ch = line[x]
+ # String?
+ if ch == '"' or ch == "'":
+ x, w = self.get_quoted_string(line, x, e)
+ s += self.as_string(w)
+ # Tab?
+ elif ch == '\t':
+ s += ' ' * 4
+ x += 1
+ # Comment?
+ elif ch == ';':
+ s += self.as_comment(line[x:])
+ # Done with this line
+ break
+ elif ch == '.' and x + 1 < e:
+ x, w = self.get_identifier(line, x + 1, e)
+ s += self.as_directive(ch + w)
+ # Identifiers?
+ elif self.is_id(ch):
+ x, w = self.get_identifier(line, x, e)
+ # Number?
+ if ch.isdigit():
+ s += self.as_num(w)
+ # Other identifier
+ else:
+ s += self.as_id(w)
+ # Output as is
+ else:
+ s += ch
+ x += 1
+ self.add_line(s)
+
+# -----------------------------------------------------------------------
+class asmview_t(idaapi.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)):
+ return False
+
+ self.instruction_list = idautils.GetInstructionList()
+ self.instruction_list.extend(["ret"])
+ self.register_list = idautils.GetRegisterList()
+ self.register_list.extend(["eax", "ebx", "ecx", "edx", "edi", "esi", "ebp", "esp"])
+
+ self.fn = fn
+ 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):
+ if not self.colorize_file(self.fn):
+ self.Close()
+ return False
+ return True
+
+ def colorize_file(self, fn):
+ try:
+ f = open(fn, "r")
+ lines = f.readlines()
+ f.close()
+ self.ClearLines()
+ self.colorize(lines)
+ return True
+ except:
+ return False
+
+ def add_line(self, s=None):
+ if not s:
+ s = ""
+ self.AddLine(s)
+
+ def as_comment(self, s):
+ return idaapi.COLSTR(s, idaapi.SCOLOR_RPTCMT)
+
+ def as_id(self, s):
+ t = s.lower()
+ if t in self.register_list:
+ return idaapi.COLSTR(s, idaapi.SCOLOR_REG)
+ elif t in self.instruction_list:
+ return idaapi.COLSTR(s, idaapi.SCOLOR_INSN)
+ else:
+ return s
+
+ def as_string(self, s):
+ return idaapi.COLSTR(s, idaapi.SCOLOR_STRING)
+
+ def as_num(self, s):
+ return idaapi.COLSTR(s, idaapi.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
+
+ def OnKeydown(self, vkey, shift):
+ """
+ User pressed a key
+ @param vkey: Virtual key code
+ @param shift: Shift flag
+ @return Boolean. True if you handled the event
+ """
+ # ESCAPE
+ if vkey == 27:
+ self.Close()
+ elif vkey == ord('H'):
+ 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
+ self.EditLine(lineno, s, fg, bg)
+ self.Refresh()
+ elif vkey == ord('C'):
+ self.ClearLines()
+ self.Refresh()
+ elif vkey == ord('S'):
+ print "Selection (x1, y1, x2, y2) = ", self.GetSelection()
+ elif vkey == ord('I'):
+ print "Position (line, x, y) = ", self.GetPos(mouse = 0)
+ else:
+ return False
+ return True
+
+# -----------------------------------------------------------------------
+class asmviewplg(idaapi.plugin_t):
+ flags = idaapi.PLUGIN_KEEP
+ comment = "ASM viewer"
+ help = "This is help"
+ wanted_name = "ASM file viewer"
+ wanted_hotkey = "Alt-F8"
+ def __init__(self):
+ self.view = None
+
+ def init(self):
+ return idaapi.PLUGIN_KEEP
+ def run(self, arg):
+ if self.view:
+ self.Close()
+ fn = idc.AskFile(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()
+
+ def term(self):
+ if self.view:
+ self.view.Close()
+
+def PLUGIN_ENTRY():
+ return asmviewplg()
\ No newline at end of file
diff --git a/Scripts/CallStackWalk.py b/Scripts/CallStackWalk.py
new file mode 100644
index 0000000..f1fef78
--- /dev/null
+++ b/Scripts/CallStackWalk.py
@@ -0,0 +1,175 @@
+"""
+
+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
+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 idautils
+
+# -----------------------------------------------------------------------
+# class to take a copy of a segment_t
+class Seg():
+ def __init__(self, s):
+ self.startEA = s.startEA
+ self.endEA = s.endEA
+ self.perm = s.perm
+ self.bitness = s.bitness
+ def __cmp__(self, other):
+ return cmp(self.startEA, other.startEA)
+
+# -----------------------------------------------------------------------
+# each item described as:
+# [ delta, [ opcode(s) ] ]
+#FF10 call d,[eax]
+#FF5000 call d,[eax][0]
+#FF9044332211 call d,[eax][011223344]
+#FF1500000100 call d,[000010000]
+#FF9300000000 call d,[ebx][0]
+#FF10 call d,[eax]
+CallPattern = \
+[
+ [-2, [0xFF] ],
+ [-3, [0xFF] ],
+ [-5, [0xE8] ],
+ [-6, [0xFF] ],
+]
+
+# -----------------------------------------------------------------------
+def IsPrevInsnCall(ea):
+ """
+ Given a return address, this function tries to check if previous instruction
+ is a CALL instruction
+ """
+ global CallPattern
+ if ea == 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)]
+ # do we have a match? is it a call instruction?
+ if bytes == opcodes and idaapi.is_call_insn(caller):
+ return caller
+ return None
+
+# -----------------------------------------------------------------------
+def CallStackWalk(nn):
+ class Result:
+ """
+ Class holding the result of one call stack item
+ Each call stack item instance has the following attributes:
+ caller = ea of caller
+ displ = display string
+ sp = stack pointer
+ """
+ def __init__(self, caller, sp):
+ self.caller = caller
+ self.sp = sp
+ f = idaapi.get_func(caller)
+ self.displ = "%08x: " % caller
+ if f:
+ self.displ += idc.GetFunctionName(caller)
+ t = caller - f.startEA
+ 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)
+ 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
+ while sp < stack_seg.endEA:
+ sp += word_size
+ ptr = idautils.GetDataList(sp, 1, word_size).next()
+ seg = idaapi.getseg(ptr)
+ # only accept executable segments
+ if (not seg) or ((seg.perm & idaapi.SEGPERM_EXEC) == 0):
+ continue
+ # try to find caller
+ caller = IsPrevInsnCall(ptr)
+ # we have no recognized caller, skip!
+ if caller is None:
+ continue
+
+ # do we have a debug name that is near?
+ if nn:
+ ret = nn.find(caller)
+ if ret:
+ ea = ret[0]
+ # function exists?
+ f = idaapi.get_func(ea)
+ if not f:
+ # create function
+ idc.MakeFunction(ea, idaapi.BADADDR)
+
+ # get the flags
+ f = idc.GetFlags(caller)
+ # no code there?
+ if not isCode(f):
+ MakeCode(caller)
+
+ callers.append(Result(caller, sp))
+ #
+ return (True, callers)
+
+# -----------------------------------------------------------------------
+# Chooser class
+class CallStackWalkChoose(Choose):
+ def __init__(self, list, title):
+ Choose.__init__(self, list, title)
+ self.width = 250
+
+ def enter(self, n):
+ o = self.list[n-1]
+ idc.Jump(o.caller)
+
+# -----------------------------------------------------------------------
+def main():
+ if not idaapi.is_debugger_on():
+ idc.Warning("Please run the process first!")
+ return
+ if idaapi.get_process_state() != -1:
+ idc.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.minEA, idaapi.cvar.inf.maxEA)
+ # initiate a nearest name search (using debug names)
+ nn = idaapi.NearestName(dn)
+ else:
+ nn = None
+
+ ret, callstack = CallStackWalk(nn)
+ if ret:
+ title = "Call stack walker (thread %X)" % (GetCurrentThreadId())
+ idaapi.close_chooser(title)
+ c = CallStackWalkChoose(callstack, title)
+ c.choose()
+ else:
+ idc.Warning("Failed to walk the stack:" + callstack)
+
+# -----------------------------------------------------------------------
+main()
\ No newline at end of file
diff --git a/Scripts/DbgCmd.py b/Scripts/DbgCmd.py
new file mode 100644
index 0000000..602566d
--- /dev/null
+++ b/Scripts/DbgCmd.py
@@ -0,0 +1,101 @@
+# -----------------------------------------------------------------------
+# Debugger command prompt with CustomViewers
+# (c) Hex-Rays
+#
+import idaapi
+import idc
+from idaapi import simplecustviewer_t
+
+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 'SendDbgCommand' IDC command.
+ """
+ s = Eval('SendDbgCommand("%s");' % cmd)
+ if s.startswith("IDC_FAILURE"):
+ raise Exception, "Debugger command is available only when the debugger is active!"
+ return s
+
+# -----------------------------------------------------------------------
+class dbgcmd_t(simplecustviewer_t):
+ def Create(self):
+ # Form the title
+ title = "Debugger command window"
+ # Create the customview
+ if not 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.askstr(0, self.last_cmd, "Please enter a debugger command")
+ if not s:
+ return
+
+ # Save last command
+ self.last_cmd = s
+
+ # Add it using a different color
+ self.AddLine("debugger>" + idaapi.COLSTR(s, idaapi.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 SendDbgCommand()", idaapi.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.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():
+ x = dbgcmd_t()
+ if not x.Create():
+ print "Failed to create debugger command line!"
+ return None
+ x.Show()
+ return x
+
+try:
+ # created already?
+ dbgcmd
+ dbgcmd.Close()
+ del dbgcmd
+except:
+ pass
+
+dbgcmd = show_win()
+if not dbgcmd:
+ del dbgcmd
+
diff --git a/Scripts/DrvsDispatch.py b/Scripts/DrvsDispatch.py
new file mode 100644
index 0000000..544e7ba
--- /dev/null
+++ b/Scripts/DrvsDispatch.py
@@ -0,0 +1,105 @@
+"""
+
+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
+ALL RIGHTS RESERVED.
+
+"""
+
+import re
+from idaapi import Choose
+
+# -----------------------------------------------------------------------
+def CmdDriverList():
+ s = Eval('WinDbgCommand("lm o");')
+ if "IDC_FAILURE" in s: return False
+ return s
+
+# -----------------------------------------------------------------------
+def CmdDrvObj(drvname, flag=2):
+ return Eval('WinDbgCommand("!drvobj %s %d");' % (drvname, flag))
+
+# -----------------------------------------------------------------------
+def CmdReloadForce():
+ s = Eval('WinDbgCommand(".reload /f");')
+ if "IDC_FAILURE" in s: return False
+ return True
+
+# -----------------------------------------------------------------------
+# class to hold dispatch entry information
+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)
+
+ # force reloading of module symbols
+ if not CmdReloadForce():
+ print "Could not communicate with WinDbg, make sure the debugger is running!"
+ return None
+
+ # get driver list
+ lm_out = CmdDriverList()
+ if not lm_out:
+ return "Failed to get driver list!"
+
+ # for each line
+ for line in lm_out.split("\n"):
+ # parse
+ r = re_drv.match(line)
+ if not r: continue
+
+ # 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):
+ def __init__(self, list, title):
+ Choose.__init__(self, list, title)
+ self.width = 250
+
+ def enter(self, n):
+ o = self.list[n-1]
+ idc.Jump(o.addr)
+
+# -----------------------------------------------------------------------
+# main
+r = GetDriverDispatch()
+if r:
+ c = DispatchChoose(r, "Dispatch table browser")
+ c.choose()
+else:
+ print "Failed to retrieve dispatchers list!"
\ No newline at end of file
diff --git a/Scripts/ExchainDump.py b/Scripts/ExchainDump.py
new file mode 100644
index 0000000..f8d93b9
--- /dev/null
+++ b/Scripts/ExchainDump.py
@@ -0,0 +1,52 @@
+"""
+
+This script shows how to send debugger commands and use the result in IDA
+
+Copyright (c) 1990-2009 Hex-Rays
+ALL RIGHTS RESERVED.
+
+"""
+
+import idc
+import re
+
+# class to store parsed results
+class exchain:
+ def __init__(self, m):
+ self.name = m.group(1)
+ self.addr = int(m.group(2), 16)
+
+ def __str__(self):
+ return "%x: %s" % (self.addr, self.name)
+
+# Chooser class
+class MyChoose(Choose):
+ def __init__(self, list, title):
+ Choose.__init__(self, list, title)
+ self.width = 250
+
+ def enter(self, n):
+ o = self.list[n-1]
+ idc.Jump(o.addr)
+
+# main
+def main():
+ s = idc.Eval('SendDbgCommand("!exchain")')
+ if "IDC_FAILURE" in s:
+ return (False, "Cannot execute the command")
+
+ matches = re.finditer(r'[^:]+: ([^\(]+) \(([^\)]+)\)\n', s)
+ L = []
+ for x in matches:
+ L.append(exchain(x))
+ if not L:
+ return (False, "Nothing to display: Could parse the result!")
+
+ # Get a Choose instance
+ chooser = MyChoose(L, "Exchain choose")
+ # Run the chooser
+ chooser.choose()
+ return (True, "Success!")
+ok, r = main()
+if not ok:
+ print r
diff --git a/Scripts/FindInstructions.py b/Scripts/FindInstructions.py
new file mode 100644
index 0000000..353d525
--- /dev/null
+++ b/Scripts/FindInstructions.py
@@ -0,0 +1,139 @@
+"""
+FindInstructions.py: 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
+
+The general syntax is:
+ find(asm or opcodes, x=Bool, asm_where=ea)
+
+* Example:
+ find("asm_statement1;asm_statement2;de ea dc 0d e0;asm_statement3;xx yy zz;...")
+* To filter-out non-executable segments pass x=True
+ find("jmp dword ptr [esp]", x=True)
+* 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
+ALL RIGHTS RESERVED.
+
+v1.0 - initial version
+"""
+import idaapi
+import idautils
+import idc
+
+# -----------------------------------------------------------------------
+def FindInstructions(instr, asm_where=None):
+ """
+ Finds instructions/opcodes
+ @return: Returns a tuple(True, [ ea, ... ]) or a tuple(False, "error message")
+ """
+ if not asm_where:
+ # get first segment
+ asm_where = FirstSeg()
+ if asm_where == idaapi.BADADDR:
+ return (False, "No segments defined")
+
+ # regular expression to distinguish between opcodes and instructions
+ re_opcode = re.compile('^[0-9a-f]{2} *', re.I)
+
+ # split lines
+ lines = instr.split(";")
+
+ # all the assembled buffers (for each instruction)
+ bufs = []
+ for line in lines:
+ if re_opcode.match(line):
+ # convert from hex string to a character list then join the list to form one string
+ buf = ''.join([chr(int(x, 16)) for x in line.split()])
+ else:
+ # assemble the instruction
+ ret, buf = Assemble(asm_where, line)
+ if not ret:
+ return (False, "Failed to assemble:"+line)
+ # add the assembled buffer
+ bufs.append(buf)
+
+ # join the buffer into one string
+ buf = ''.join(bufs)
+
+ # take total assembled instructions length
+ tlen = len(buf)
+
+ # convert from binary string to space separated hex string
+ bin_str = ' '.join(["%02X" % ord(x) for x in buf])
+
+ # find all binary strings
+ print "Searching for: [%s]" % bin_str
+ ea = MinEA()
+ ret = []
+ while True:
+ ea = FindBinary(ea, SEARCH_DOWN, bin_str)
+ if ea == idaapi.BADADDR:
+ break
+ ret.append(ea)
+ Message(".")
+ ea += tlen
+ if not ret:
+ return (False, "Could not match [%s]" % bin_str)
+ Message("\n")
+ return (True, ret)
+
+# -----------------------------------------------------------------------
+# Chooser class
+class SearchResultChoose(Choose):
+ def __init__(self, list, title):
+ Choose.__init__(self, list, title)
+ self.width = 250
+
+ def enter(self, n):
+ o = self.list[n-1]
+ Jump(o.ea)
+
+# -----------------------------------------------------------------------
+# class to represent the results
+class SearchResult:
+ def __init__(self, ea):
+ self.ea = ea
+ if not isCode(GetFlags(ea)):
+ MakeCode(ea)
+ t = idaapi.generate_disasm_line(ea)
+ if t:
+ line = idaapi.tag_remove(t)
+ else:
+ line = ""
+ func = GetFunctionName(ea)
+ self.display = hex(ea) + ": "
+ if func:
+ self.display += func + ": "
+ else:
+ n = SegName(ea)
+ if n: self.display += n + ": "
+ self.display += line
+
+ def __str__(self):
+ return self.display
+
+# -----------------------------------------------------------------------
+def find(s=None, x=False, asm_where=None):
+ b, ret = FindInstructions(s, asm_where)
+ if b:
+ # executable segs only?
+ if x:
+ results = []
+ for ea in ret:
+ seg = idaapi.getseg(ea)
+ if (not seg) or (seg.perm & idaapi.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)
+ c = SearchResultChoose(results, title)
+ c.choose()
+ else:
+ print ret
+
+# -----------------------------------------------------------------------
+print "Please use find('asm_stmt1;xx yy;...', x=Bool,asm_where=ea) to search for instructions or opcodes. Specify x=true to filter out non-executable segments"
diff --git a/Scripts/ImpRef.py b/Scripts/ImpRef.py
new file mode 100644
index 0000000..5dc8542
--- /dev/null
+++ b/Scripts/ImpRef.py
@@ -0,0 +1,69 @@
+# -----------------------------------------------------------------------
+# This is an example illustrating how to enumerate all addresses
+# that refer to all imported functions in a given module
+#
+# (c) Hex-Rays
+#
+
+import idaapi
+import idc
+import idautils
+import re
+
+# -----------------------------------------------------------------------
+def find_imported_funcs(dllname):
+ def imp_cb(ea, name, ord):
+ if not name:
+ name = ''
+ imports.append([ea, name, ord])
+ return True
+
+ imports = []
+ nimps = idaapi.get_import_module_qty()
+ for i in xrange(0, nimps):
+ name = idaapi.get_import_module_name(i)
+ if re.match(dllname, name, re.IGNORECASE) is None:
+ continue
+ idaapi.enum_import_names(i, imp_cb)
+
+ return imports
+
+
+# -----------------------------------------------------------------------
+def find_import_ref(dllname):
+ imports = find_imported_funcs(dllname)
+ R = dict()
+ for i, (ea, name,_) in enumerate(imports):
+ #print "%x -> %s" % (ea, name)
+ 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.startEA, idaapi.get_func_name(f.startEA), 0])
+ #print "\t%x %s: from a thunk, parent added %x" % (ea, name, f.startEA)
+ continue
+
+ # save results
+ if not R.has_key(i):
+ R[i] = []
+
+ R[i].append(ea)
+
+ return (imports, R)
+
+# -----------------------------------------------------------------------
+def main():
+ dllname = idc.AskStr('kernel32', "Enter module name")
+ if not dllname:
+ print("Cancelled")
+ return
+
+ imports, R = find_import_ref(dllname)
+ for k, v in R.items():
+ print(imports[k][1])
+ for ea in v:
+ print("\t%x" % ea)
+
+# -----------------------------------------------------------------------
+main()
\ No newline at end of file
diff --git a/Scripts/ImportExportViewer.py b/Scripts/ImportExportViewer.py
new file mode 100644
index 0000000..afe0aac
--- /dev/null
+++ b/Scripts/ImportExportViewer.py
@@ -0,0 +1,124 @@
+# -----------------------------------------------------------------------
+# This is an example illustrating how to:
+# - enumerate imports
+# - enumerate entrypoints
+# - Use PluginForm class
+# - Use PySide with PluginForm to create a Python UI
+#
+# (c) Hex-Rays
+#
+import idaapi
+import idautils
+from idaapi import PluginForm
+from PySide import QtGui, QtCore
+
+# --------------------------------------------------------------------------
+class ImpExpForm_t(PluginForm):
+
+ def imports_names_cb(self, ea, name, ord):
+ self.items.append((ea, '' if not name else name, ord))
+ # True -> Continue enumeration
+ return True
+
+
+ def BuildImports(self):
+ tree = {}
+ nimps = idaapi.get_import_module_qty()
+
+ for i in xrange(0, nimps):
+ name = idaapi.get_import_module_name(i)
+ if not name:
+ continue
+ # Create a list for imported names
+ self.items = []
+
+ # Enum imported entries in this module
+ idaapi.enum_import_names(i, self.imports_names_cb)
+
+ if name not in tree:
+ tree[name] = []
+ tree[name].extend(self.items)
+
+ return tree
+
+
+ def BuildExports(self):
+ return list(idautils.Entries())
+
+
+ def PopulateTree(self):
+ # Clear previous items
+ self.tree.clear()
+
+ # Build imports
+ root = QtGui.QTreeWidgetItem(self.tree)
+ root.setText(0, "Imports")
+
+ for dll_name, imp_entries in self.BuildImports().items():
+ imp_dll = QtGui.QTreeWidgetItem(root)
+ imp_dll.setText(0, dll_name)
+
+ for imp_ea, imp_name, imp_ord in imp_entries:
+ item = QtGui.QTreeWidgetItem(imp_dll)
+ item.setText(0, "%s [0x%08x]" %(imp_name, imp_ea))
+
+
+ # Build exports
+ root = QtGui.QTreeWidgetItem(self.tree)
+ root.setText(0, "Exports")
+
+ for exp_i, exp_ord, exp_ea, exp_name in self.BuildExports():
+ item = QtGui.QTreeWidgetItem(root)
+ item.setText(0, "%s [#%d] [0x%08x]" % (exp_name, exp_ord, exp_ea))
+
+
+ def OnCreate(self, form):
+ """
+ Called when the plugin form is created
+ """
+
+ # Get parent widget
+ self.parent = self.FormToPySideWidget(form)
+
+ # Create tree control
+ self.tree = QtGui.QTreeWidget()
+ self.tree.setHeaderLabels(("Names",))
+ self.tree.setColumnWidth(0, 100)
+
+ # Create layout
+ layout = QtGui.QVBoxLayout()
+ layout.addWidget(self.tree)
+
+ self.PopulateTree()
+ # Populate PluginForm
+ self.parent.setLayout(layout)
+
+
+ def OnClose(self, form):
+ """
+ Called when the plugin form is closed
+ """
+ global ImpExpForm
+ del ImpExpForm
+ print "Closed"
+
+
+ def Show(self):
+ """Creates the form is not created or focuses it if it was"""
+ return PluginForm.Show(self,
+ "Imports / Exports viewer",
+ options = PluginForm.FORM_PERSIST)
+
+# --------------------------------------------------------------------------
+def main():
+ global ImpExpForm
+
+ try:
+ ImpExpForm
+ except:
+ ImpExpForm = ImpExpForm_t()
+
+ ImpExpForm.Show()
+
+# --------------------------------------------------------------------------
+main()
\ No newline at end of file
diff --git a/Scripts/PteDump.py b/Scripts/PteDump.py
new file mode 100644
index 0000000..23e37b5
--- /dev/null
+++ b/Scripts/PteDump.py
@@ -0,0 +1,85 @@
+import idaapi
+import idc
+from idaapi import Choose2
+
+def parse_pte(str):
+ try:
+ parse_pte.re
+ except:
+ parse_pte.re = re.compile('PDE at ([0-9a-f]+)\s*PTE at ([0-9a-f]+)\ncontains ([0-9a-f]+)\s*contains ([0-9a-f]+)\npfn ([0-9]+)\s*([^ ]+)\s*pfn ([0-9a-f]+)\s*([^\r\n]+)', re.I | re.M)
+ parse_pte.items = ('pde', 'pte', 'pdec', 'ptec', 'pdepfn', 'pdepfns', 'ptepfn', 'ptepfns')
+
+ m = parse_pte.re.search(s)
+ r = {}
+ for i in range(0, len(parse_pte.items)):
+ r[parse_pte.items[i]] = m.group(i+1)
+ return r
+
+class MyChoose2(Choose2):
+
+ def __init__(self, title, ea1, ea2):
+ Choose2.__init__(self, title, [ ["VA", 10], ["PTE attr", 30] ])
+ self.ea1 = ea1
+ self.ea2 = ea2
+ self.n = 0
+ self.icon = 5
+ self.items = []
+ self.Refresh()
+ self.selcount = 0
+
+ def OnGetLine(self, n):
+ print("getline %d" % n)
+ return self.items[n]
+
+ def OnGetSize(self):
+ n = len(self.items)
+ self.Refresh()
+ return n
+
+ def OnRefresh(self, n):
+ print("refresh %d" % n)
+ return n
+
+ def Refresh(self):
+ items = []
+ PG = 0x1000
+ ea1 = self.ea1
+ npages = (self.ea2 - ea1) / PG
+ for i in range(npages):
+ r = idc.SendDbgCommand("!pte %x" % ea1)
+ if not r:
+ return False
+ r = parse_pte(r)
+ items.append([hex(ea1), r['ptepfns']])
+ ea1 += PG
+
+ self.items = items
+ print(self.items)
+ return True
+
+ @staticmethod
+ def Execute(ea1, ea2):
+ c = MyChoose2("PTE Viewer [%x..%x]" % (ea1, ea2), ea1, ea2)
+ return (c, c.Show())
+
+
+def DumpPTE(ea1, ea2):
+ items = []
+ PG = 0x1000
+ npages = (ea2 - ea1) / PG
+ for i in range(npages):
+ r = idc.SendDbgCommand("!pte %x" % ea1)
+ if not r:
+ return False
+ print r
+ r = parse_pte(r)
+ print("VA: %08X PTE: %s PDE: %s" % (ea1, r['ptepfns'], r['pdepfns']))
+ ea1 += PG
+
+def DumpSegPTE(ea):
+ DumpPTE(idc.SegStart(ea), idc.SegEnd(ea))
+
+DumpSegPTE(here())
+
+#MyChoose2.Execute(0xF718F000, 0xF718F000+0x1000)
+
diff --git a/Scripts/SEHGraph.py b/Scripts/SEHGraph.py
new file mode 100644
index 0000000..9cd3216
--- /dev/null
+++ b/Scripts/SEHGraph.py
@@ -0,0 +1,149 @@
+"""
+
+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
+ALL RIGHTS RESERVED.
+
+
+v1.0 - initial version
+
+"""
+
+import idaapi
+import idautils
+import idc
+
+from idaapi import GraphViewer
+
+# -----------------------------------------------------------------------
+# 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('WinDbgCommand("dg %x")' % cpu.fs)
+ if "IDC_FAILURE" in s:
+ return 0
+ m = re.compile("[0-9a-f]{4} ([0-9a-f]{8})")
+ t = m.match(s.split('\n')[-2])
+ if not t:
+ return 0
+ return int(t.group(1), 16)
+
+# -----------------------------------------------------------------------
+def GetFsBase(tid):
+ idc.SelectThread(tid)
+ base = idaapi.dbg_get_thread_sreg_base(tid, cpu.fs)
+ if base != 0:
+ return base
+ return WindbgGetRegBase(tid)
+
+# -----------------------------------------------------------------------
+# Walks the SEH chain and returns a list of handlers
+def GetExceptionChain(tid):
+ fs_base = GetFsBase(tid)
+ exc_rr = Dword(fs_base)
+ result = []
+ while exc_rr != 0xffffffff:
+ prev = Dword(exc_rr)
+ handler = Dword(exc_rr + 4)
+ exc_rr = prev
+ result.append(handler)
+ return result
+
+# -----------------------------------------------------------------------
+class SEHGraph(GraphViewer):
+ def __init__(self, title, result):
+ GraphViewer.__init__(self, title)
+ self.result = result
+ self.names = {} # ea -> name
+
+ def OnRefresh(self):
+ self.Clear()
+ addr_id = {}
+
+ for (tid, chain) in self.result.items():
+ # Each node data will contain a tuple of the form: (Boolean->Is_thread, Int->Value, String->Label)
+ # For threads the is_thread will be true and the value will hold the thread id
+ # For exception handlers, is_thread=False and Value=Handler address
+
+ # Add the thread node
+ id_parent = self.AddNode( (True, tid, "Thread %X" % tid) )
+
+ # Add each handler
+ for handler in chain:
+ # Check if a function is created at the handler's address
+ f = idaapi.get_func(handler)
+ if not f:
+ # create function
+ idc.MakeFunction(handler, idaapi.BADADDR)
+
+ # Node label is function name or address
+ s = GetFunctionName(handler)
+ if not s:
+ s = "%x" % handler
+
+ # cache name
+ self.names[handler] = s
+
+ # Get the node id given the handler address
+ # We use an addr -> id dictionary so that similar addresses get similar node id
+ if not addr_id.has_key(handler):
+ id = self.AddNode( (False, handler, s) )
+ addr_id[handler] = id # add this ID
+ else:
+ id = addr_id[handler]
+
+ # Link handlers to each other
+ self.AddEdge(id_parent, id)
+ id_parent = id
+
+ return True
+
+ def OnGetText(self, node_id):
+ is_thread, value, label = self[node_id]
+ if is_thread:
+ return (label, 0xff00f0)
+ return label
+
+ def OnDblClick(self, node_id):
+ is_thread, value, label = self[node_id]
+ if is_thread:
+ idc.SelectThread(value)
+ self.Show()
+ s = "SEH chain for " + hex(value)
+ t = "-" * len(s)
+ print t
+ print s
+ print t
+ for handler in self.result[value]:
+ print "%x: %s" % (handler, self.names[handler])
+ print t
+ else:
+ idc.Jump(value)
+ return True
+
+
+# -----------------------------------------------------------------------
+def main():
+ if not idaapi.dbg_can_query():
+ print "The debugger must be active and suspended before using this script!"
+ return
+
+ # Save current thread id
+ tid = GetCurrentThreadId()
+
+ # Iterate through all function instructions and take only call instructions
+ result = {}
+ for tid in idautils.Threads():
+ result[tid] = GetExceptionChain(tid)
+
+ # Restore previously selected thread
+ idc.SelectThread(tid)
+
+ # Build the graph
+ g = SEHGraph("SEH graph", result)
+ g.Show()
+
+main()
diff --git a/Scripts/VaDump.py b/Scripts/VaDump.py
new file mode 100644
index 0000000..cf606eb
--- /dev/null
+++ b/Scripts/VaDump.py
@@ -0,0 +1,70 @@
+"""
+
+This script shows how to send debugger commands and use the result in IDA
+
+Copyright (c) 1990-2009 Hex-Rays
+ALL RIGHTS RESERVED.
+
+"""
+
+import idc
+from idaapi import Choose
+
+import re
+
+# class to store parsed results
+class memva:
+ def __init__(self, m):
+ self.base = int(m.group(1), 16)
+ self.regionsize = int(m.group(2), 16)
+ self.state = int(m.group(3), 16)
+ self.statestr = m.group(4).strip()
+ self.protect = int(m.group(5), 16)
+ self.protectstr = m.group(6).strip()
+ if m.group(7):
+ self.type = int(m.group(8), 16)
+ self.typestr = m.group(9).strip()
+ else:
+ self.type = 0
+ self.typestr = ""
+ def __str__(self):
+ return "(Base %08X; RegionSize: %08X; State: %08X/%10s; protect: %08X/%10s; type: %08X/%10s)" % (
+ self.base, self.regionsize, self.state,
+ self.statestr, self.protect,
+ self.protectstr, self.type, self.typestr)
+
+# Chooser class
+class MemChoose(Choose):
+ def __init__(self, list, title):
+ Choose.__init__(self, list, title)
+ self.width = 250
+
+ def enter(self, n):
+ o = self.list[n-1]
+ idc.Jump(o.base)
+
+# main
+def main():
+ s = idc.Eval('SendDbgCommand("!vadump")')
+ if "IDC_FAILURE" in s:
+ return (False, "Cannot execute the command")
+
+ matches = re.finditer(r'BaseAddress:\s*?(\w+?)\n' \
+ +'RegionSize:\s*?(\w*?)\n' \
+ +'State:\s*?(\w*?)\s*?(\w*?)\n' \
+ +'Protect:\s*?(\w*?)\s*?(\w*?)\n' \
+ +'(Type:\s*?(\w*?)\s*?(\w*?)\n)*', s)
+ L = []
+ for x in matches:
+ L.append(memva(x))
+ if not L:
+ return (False, "Nothing to display: Could not parse the result!")
+
+ # Get a Choose instance
+ chooser = MemChoose(L, "Memory choose")
+ # Run the chooser
+ chooser.choose()
+ return (True, "Success!")
+r = main()
+if not r[0]:
+ print r[1]
diff --git a/Scripts/msdnapihelp.py b/Scripts/msdnapihelp.py
new file mode 100644
index 0000000..08117e3
--- /dev/null
+++ b/Scripts/msdnapihelp.py
@@ -0,0 +1,68 @@
+"""
+User contributed script: MSDN API HELP plugin
+
+This script fetches the API reference (from MSDN) of a given highlighted identifier
+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
+
+# -----------------------------------------------------------------------
+class msdnapihelp_plugin_t(idaapi.plugin_t):
+ flags = 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
+
+
+ @staticmethod
+ def sanitize_name(name):
+ t = idaapi.FUNC_IMPORT_PREFIX
+ if name.startswith(t):
+ return name[len(t):]
+ return name
+
+
+ def run(self, arg):
+ # Get the highlighted identifier
+ id = idaapi.get_highlighted_identifier()
+ if not id:
+ 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)
+ if len(d['entries']) > 0:
+ url = d['entries'][0].link
+ webbrowser.open_new_tab(url)
+ else:
+ print "API documentation not found for: %s" % id
+
+
+ def term(self):
+ pass
+
+
+# -----------------------------------------------------------------------
+def PLUGIN_ENTRY():
+ return msdnapihelp_plugin_t()
diff --git a/build.py b/build.py
index 9ff09e6..69c724a 100644
--- a/build.py
+++ b/build.py
@@ -24,7 +24,7 @@ from distutils import sysconfig
VERBOSE = True
IDA_MAJOR_VERSION = 6
-IDA_MINOR_VERSION = 0
+IDA_MINOR_VERSION = 1
if 'IDA' in os.environ:
IDA_SDK = os.environ['IDA']
@@ -35,8 +35,8 @@ else:
# IDAPython version
VERSION_MAJOR = 1
-VERSION_MINOR = 4
-VERSION_PATCH = 3
+VERSION_MINOR = 5
+VERSION_PATCH = 0
# Determine Python version
PYTHON_MAJOR_VERSION = int(platform.python_version()[0])
@@ -68,6 +68,7 @@ BINDIST_MANIFEST = [
"CHANGES.txt",
"AUTHORS.txt",
"STATUS.txt",
+ "python.cfg",
"docs/notes.txt",
"examples/chooser.py",
"examples/colours.py",
@@ -91,6 +92,8 @@ BINDIST_MANIFEST = [
"examples/ex_prefix_plugin.py",
"examples/ex_pyside.py",
"examples/ex_pyqt.py",
+ "examples/ex_askusingform.py",
+ "examples/ex_uihook.py",
"examples/ex_imports.py"
]
@@ -100,6 +103,7 @@ SRCDIST_MANIFEST = [
"python.cpp",
"basetsd.h",
"build.py",
+ "python.cfg",
"swig/allins.i",
"swig/area.i",
"swig/auto.i",
@@ -309,7 +313,7 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64):
platform_macros = [ "__LINUX__" ]
python_libpath = sysconfig.EXEC_PREFIX + os.sep + "lib"
python_library = "-lpython%d.%d" % (PYTHON_MAJOR_VERSION, PYTHON_MINOR_VERSION)
- ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "gcc64.lnx" or "gcc32.lnx")
+ ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "x86_linux_gcc_64" or "x86_linux_gcc_32")
ida_lib = ""
extra_link_parameters = ""
# Platform-specific settings for the Windows build
@@ -318,7 +322,7 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64):
platform_macros = [ "__NT__" ]
python_libpath = sysconfig.EXEC_PREFIX + os.sep + "libs"
python_library = "python%d%d.lib" % (PYTHON_MAJOR_VERSION, PYTHON_MINOR_VERSION)
- ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "vc.w64" or "vc.w32")
+ ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "x86_win_vc_64" or "x86_win_vc_32")
ida_lib = "ida.lib"
SWIG_OPTIONS += " -D__NT__ "
extra_link_parameters = ""
@@ -329,7 +333,7 @@ def build_plugin(platform, idasdkdir, plugin_name, ea64):
platform_macros = [ "__MAC__" ]
python_libpath = "."
python_library = "-framework Python"
- ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "gcc64.mac64" or "gcc32.mac")
+ ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "x86_mac_gcc_64" or "x86_mac_gcc_32")
ida_lib = ea64 and "-lida64" or "-lida"
extra_link_parameters = ""
diff --git a/examples/ex1_idaapi.py b/examples/ex1_idaapi.py
index 3fec86a..8902ab8 100644
--- a/examples/ex1_idaapi.py
+++ b/examples/ex1_idaapi.py
@@ -7,23 +7,27 @@
#
from idaapi import *
-# Get current ea
-ea = get_screen_ea()
+def main():
+ # Get current ea
+ ea = get_screen_ea()
-# Get segment class
-seg = getseg(ea)
+ # Get segment class
+ seg = getseg(ea)
-# Loop from segment start to end
-func = get_func(seg.startEA)
+ # Loop from segment start to end
+ func = get_next_func(seg.startEA)
+ seg_end = seg.endEA
+ while func is not None and func.startEA < seg_end:
+ funcea = func.startEA
+ print "Function %s at 0x%x" % (GetFunctionName(funcea), funcea)
-while func != None and func.startEA < seg.endEA:
- funcea = func.startEA
- print "Function %s at 0x%x" % (GetFunctionName(funcea), funcea)
+ ref = get_first_cref_to(funcea)
- ref = get_first_cref_to(funcea)
+ while ref != BADADDR:
+ print " called from %s(0x%x)" % (get_func_name(ref), ref)
+ ref = get_next_cref_to(funcea, ref)
- while ref != BADADDR:
- print " called from %s(0x%x)" % (get_func_name(ref), ref)
- ref = get_next_cref_to(funcea, ref)
+ func = get_next_func(funcea)
- func = get_next_func(funcea)
+
+main()
\ No newline at end of file
diff --git a/examples/ex1_idautils.py b/examples/ex1_idautils.py
index d0d76b7..5072f94 100644
--- a/examples/ex1_idautils.py
+++ b/examples/ex1_idautils.py
@@ -7,14 +7,21 @@
#
from idautils import *
-# Get current ea
-ea = ScreenEA()
+def main():
+ # Get current ea
+ ea = ScreenEA()
+ if ea == idaapi.BADADDR:
+ print("Could not get get_screen_ea()")
+ return
-# Loop from start to end in the current segment
-for funcea in Functions(SegStart(ea), SegEnd(ea)):
- print "Function %s at 0x%x" % (GetFunctionName(funcea), funcea)
+ # Loop from start to end in the current segment
+ for funcea in Functions(SegStart(ea), SegEnd(ea)):
+ print("Function %s at 0x%x" % (GetFunctionName(funcea), funcea))
- # Find all code references to funcea
- for ref in CodeRefsTo(funcea, 1):
- print " called from %s(0x%x)" % (GetFunctionName(ref), ref)
+ # Find all code references to funcea
+ for ref in CodeRefsTo(funcea, 1):
+ print(" called from %s(0x%x)" % (GetFunctionName(ref), ref))
+
+if __name__=='__main__':
+ main()
\ No newline at end of file
diff --git a/examples/ex_add_menu_item.py b/examples/ex_add_menu_item.py
index 0b681bc..e9bed27 100644
--- a/examples/ex_add_menu_item.py
+++ b/examples/ex_add_menu_item.py
@@ -1,18 +1,18 @@
-import idaapi
-
-def cb(*args):
- print "Callback called!"
- return 1
-
-try:
- if ctx:
- idaapi.del_menu_item(ctx)
-except:
- pass
-
-ctx = idaapi.add_menu_item("Search/", "X", "", 0, cb, tuple("hello world"))
-if ctx is None:
- print "Failed to add menu!"
- del ctx
-else:
- print "Menu added successfully!"
\ No newline at end of file
+import idaapi
+
+def cb(*args):
+ print("Callback called!")
+ return 1
+
+try:
+ ctx
+ idaapi.del_menu_item(ctx)
+ print("Menu removed")
+ del ctx
+except:
+ ctx = idaapi.add_menu_item("Search/", "X", "", 0, cb, tuple("hello world"))
+ if ctx is None:
+ print("Failed to add menu!")
+ del ctx
+ else:
+ print("Menu added successfully. Run the script again to delete the menu")
\ No newline at end of file
diff --git a/examples/ex_askusingform.py b/examples/ex_askusingform.py
new file mode 100644
index 0000000..9c6f475
--- /dev/null
+++ b/examples/ex_askusingform.py
@@ -0,0 +1,221 @@
+# -----------------------------------------------------------------------
+# This is an example illustrating how to use the Form class
+# (c) Hex-Rays
+#
+from idaapi import Form
+
+#
+# --------------------------------------------------------------------------
+class TestEmbeddedChooserClass(Choose2):
+ """
+ A simple chooser to be used as an embedded chooser
+ """
+ def __init__(self, title, nb = 5, flags=0):
+ Choose2.__init__(self,
+ title,
+ [ ["Address", 10], ["Name", 30] ],
+ embedded=True, width=30, height=20, flags=flags)
+ self.n = 0
+ self.items = [ self.make_item() for x in xrange(0, nb+1) ]
+ self.icon = 5
+ self.selcount = 0
+
+ def make_item(self):
+ r = [str(self.n), "func_%04d" % self.n]
+ self.n += 1
+ return r
+
+ def OnClose(self):
+ pass
+
+ def OnGetLine(self, n):
+ print("getline %d" % n)
+ return self.items[n]
+
+ def OnGetSize(self):
+ n = len(self.items)
+ print("getsize -> %d" % n)
+ return n
+
+# --------------------------------------------------------------------------
+class MyForm(Form):
+ def __init__(self):
+ self.invert = False
+ self.EChooser = TestEmbeddedChooserClass("E1", flags=Choose2.CH_MULTI)
+ Form.__init__(self, r"""STARTITEM {id:rNormal}
+BUTTON YES* Yeah
+BUTTON NO Nope
+BUTTON CANCEL Nevermind
+Form Test
+
+{FormChangeCb}
+This is a string: +{cStr1}+
+This is an address: +{cAddr1}+
+
+Escape\{control}
+This is a string: '{cStr2}'
+This is a number: {cVal1}
+
+<#Hint1#Enter name:{iStr1}>
+<#Hint2#Select color:{iColor1}>
+Browse test
+<#Select a file to open#Browse to open:{iFileOpen}>
+<#Select a file to save#Browse to save:{iFileSave}>
+<#Select dir#Browse for dir:{iDir}>
+Type
+<#Select type#Write a type:{iType}>
+Numbers
+<##Enter a selector value:{iSegment}>
+<##Enter a raw hex:{iRawHex}>
+<##Enter a character:{iChar}>
+<##Enter an address:{iAddr}>
+Button test
+<##Button1:{iButton1}> <##Button2:{iButton2}>
+
+Check boxes:
+
+
+{cGroup1}>
+
+Radio boxes:
+
+
+{cGroup2}>
+
+The end!
+""", {
+ 'cStr1': Form.StringLabel("Hello"),
+ 'cStr2': Form.StringLabel("StringTest"),
+ 'cAddr1': Form.NumericLabel(0x401000, Form.FT_ADDR),
+ 'cVal1' : Form.NumericLabel(99, Form.FT_HEX),
+ 'iStr1': Form.StringInput(),
+ 'iColor1': Form.ColorInput(),
+ 'iFileOpen': Form.FileInput(open=True),
+ 'iFileSave': Form.FileInput(save=True),
+ 'iDir': Form.DirInput(),
+ 'iType': Form.StringInput(tp=Form.FT_TYPE),
+ 'iSegment': Form.NumericInput(tp=Form.FT_SEG),
+ 'iRawHex': Form.NumericInput(tp=Form.FT_RAWHEX),
+ 'iAddr': Form.NumericInput(tp=Form.FT_ADDR),
+ 'iChar': Form.NumericInput(tp=Form.FT_CHAR),
+ 'iButton1': Form.ButtonInput(self.OnButton1),
+ 'iButton2': Form.ButtonInput(self.OnButton2),
+ 'cGroup1': Form.ChkGroupControl(("rNormal", "rError", "rWarnings")),
+ 'cGroup2': Form.RadGroupControl(("rRed", "rGreen", "rBlue")),
+ 'FormChangeCb': Form.FormChangeCb(self.OnFormChange),
+ 'cEChooser' : Form.EmbeddedChooserControl(self.EChooser)
+ })
+
+
+ def OnButton1(self, code=0):
+ print("Button1 pressed")
+
+
+ def OnButton2(self, code=0):
+ print("Button2 pressed")
+
+
+ def OnFormChange(self, fid):
+ if fid == self.iButton1.id:
+ print("Button1 fchg;inv=%s" % self.invert)
+ self.SetFocusedField(self.rNormal.id)
+ self.EnableField(self.rError.id, self.invert)
+ self.invert = not self.invert
+ elif fid == self.iButton2.id:
+ g1 = self.GetControlValue(self.cGroup1)
+ g2 = self.GetControlValue(self.cGroup2)
+ d = self.GetControlValue(self.iDir)
+ f = self.GetControlValue(self.iFileOpen)
+ print("cGroup2:%x;Dir=%s;fopen=%s;cGroup1:%x" % (g1, d, f, g2))
+ elif fid == self.cEChooser.id:
+ l = self.GetControlValue(self.cEChooser)
+ print("Chooser: %s" % l)
+ else:
+ print(">>fid:%d" % fid)
+ return 1
+
+# --------------------------------------------------------------------------
+def stdalone_main():
+ f = MyForm()
+ f, args = f.Compile()
+ print args[0]
+ print args[1:]
+ f.rNormal.checked = True
+ f.rWarnings.checked = True
+ print hex(f.cGroup1.value)
+
+ f.rGreen.selected = True
+ print f.cGroup2.value
+ print "Title: '%s'" % f.title
+
+ f.Free()
+
+# --------------------------------------------------------------------------
+def ida_main():
+ # Create form
+ global f
+ f = MyForm()
+
+ # Compile (in order to populate the controls)
+ f.Compile()
+
+ f.iColor1.value = 0x5bffff
+ f.iDir.value = os.getcwd()
+ f.rNormal.checked = True
+ f.rWarnings.checked = True
+ f.rGreen.selected = True
+ f.iStr1.value = "Hello"
+
+ # Execute the form
+ ok = f.Execute()
+ print("r=%d" % ok)
+ if ok == 1:
+ print("f.str1=%s" % f.iStr1.value)
+ print("f.color1=%x" % f.iColor1.value)
+ print("f.openfile=%s" % f.iFileOpen.value)
+ print("f.savefile=%s" % f.iFileSave.value)
+ print("f.dir=%s" % f.iDir.value)
+ print("f.type=%s" % f.iType.value)
+ print("f.seg=%s" % f.iSegment.value)
+ print("f.rawhex=%x" % f.iRawHex.value)
+ print("f.char=%x" % f.iChar.value)
+ print("f.addr=%x" % f.iAddr.value)
+ print("f.cGroup1=%x" % f.cGroup1.value)
+ print("f.cGroup2=%x" % f.cGroup2.value)
+
+ sel = f.EChooser.GetEmbSelection()
+ if sel is None:
+ print("No selection")
+ else:
+ print("Selection: %s" % sel)
+ # Dispose the form
+ f.Free()
+
+# --------------------------------------------------------------------------
+def ida_main_legacy():
+ # Here we simply show how to use the old style form format using Python
+
+ # Sample form from kernwin.hpp
+ s = """Sample dialog box
+
+
+This is sample dialog box for %A
+using address %$
+
+<~E~nter value:N:32:16::>
+"""
+
+ # Use either StringArgument or NumericArgument to pass values to the function
+ num = Form.NumericArgument('N', value=123)
+ ok = idaapi.AskUsingForm(s,
+ Form.StringArgument("PyAskUsingForm").arg,
+ Form.NumericArgument('$', 0x401000).arg,
+ num.arg)
+ if ok == 1:
+ print("You entered: %x" % num.value)
+
+#
+
+
+# --------------------------------------------------------------------------
+ida_main()
\ No newline at end of file
diff --git a/examples/ex_choose2.py b/examples/ex_choose2.py
index 69efc4f..5cc2ddc 100644
--- a/examples/ex_choose2.py
+++ b/examples/ex_choose2.py
@@ -1,84 +1,84 @@
-import idaapi
-from idaapi import Choose2
-
-class MyChoose2(Choose2):
-
- def __init__(self, title, nb = 5):
- Choose2.__init__(self, title, [ ["Address", 10], ["Name", 30] ])
- self.n = 0
- self.items = [ self.make_item() for x in xrange(0, nb+1) ]
- self.icon = 5
- self.selcount = 0
- self.popup_names = ["Inzert", "Del leet", "Ehdeet", "Ree frech"]
- print "created", str(self)
-
- def OnClose(self):
- print "closed", str(self)
-
- def OnEditLine(self, n):
- self.items[n][1] = self.items[n][1] + "*"
- print "editing", str(n)
-
- def OnInsertLine(self):
- self.items.append(self.make_item())
- print "insert line"
-
- def OnSelectLine(self, n):
- self.selcount += 1
- Warning("[%02d] selectline '%s'" % (self.selcount, n))
-
- def OnGetLine(self, n):
- print "getline", str(n)
- return self.items[n]
-
- def OnGetSize(self):
- print "getsize"
- return len(self.items)
-
- def OnDeleteLine(self, n):
- print "del ",str(n)
- del self.items[n]
- return n
-
- def OnRefresh(self, n):
- print "refresh", n
- return n
-
- def OnCommand(self, n, cmd_id):
- if cmd_id == self.cmd_a:
- print "command A selected @", n
- elif cmd_id == self.cmd_b:
- print "command B selected @", n
- else:
- print "Unknown command:", cmd_id, "@", n
- return 1
-
- def OnGetIcon(self, n):
- r = self.items[n]
- t = self.icon + r[1].count("*")
- print "geticon", n, t
- return t
-
- def show(self):
- t = self.Show()
- if t < 0:
- return False
- self.cmd_a = self.AddCommand("command A")
- self.cmd_b = self.AddCommand("command B")
- return True
-
- def make_item(self):
- r = [str(self.n), "func_%04d" % self.n]
- self.n += 1
- return r
-
- def OnGetLineAttr(self, n):
- print "getlineattr", n
- if n == 1:
- return [0xFF0000, 0]
-
-for i in xrange(1, 5+1):
- c = MyChoose2("choose2 - sample %d" % i, i*2)
- r = c.show()
- print r
-
\ No newline at end of file
+import idaapi
+from idaapi import Choose2
+
+class MyChoose2(Choose2):
+
+ def __init__(self, title, nb = 5):
+ Choose2.__init__(self, title, [ ["Address", 10], ["Name", 30] ])
+ self.n = 0
+ self.items = [ self.make_item() for x in xrange(0, nb+1) ]
+ self.icon = 5
+ self.selcount = 0
+ self.popup_names = ["Inzert", "Del leet", "Ehdeet", "Ree frech"]
+ print("created %s" % str(self))
+
+ def OnClose(self):
+ print "closed", str(self)
+
+ def OnEditLine(self, n):
+ self.items[n][1] = self.items[n][1] + "*"
+ print("editing %d" % n)
+
+ def OnInsertLine(self):
+ self.items.append(self.make_item())
+ print("insert line")
+
+ def OnSelectLine(self, n):
+ self.selcount += 1
+ Warning("[%02d] selectline '%s'" % (self.selcount, n))
+
+ def OnGetLine(self, n):
+ print("getline %d" % n)
+ return self.items[n]
+
+ def OnGetSize(self):
+ n = len(self.items)
+ print("getsize -> %d" % n)
+ return n
+
+ def OnDeleteLine(self, n):
+ print("del %d " % n)
+ del self.items[n]
+ return n
+
+ def OnRefresh(self, n):
+ print("refresh %d" % n)
+ return n
+
+ def OnCommand(self, n, cmd_id):
+ if cmd_id == self.cmd_a:
+ print "command A selected @", n
+ elif cmd_id == self.cmd_b:
+ print "command B selected @", n
+ else:
+ print "Unknown command:", cmd_id, "@", n
+ return 1
+
+ def OnGetIcon(self, n):
+ r = self.items[n]
+ t = self.icon + r[1].count("*")
+ print "geticon", n, t
+ return t
+
+ def show(self):
+ t = self.Show()
+ if t < 0:
+ return False
+ self.cmd_a = self.AddCommand("command A")
+ self.cmd_b = self.AddCommand("command B")
+ return True
+
+ def make_item(self):
+ r = [str(self.n), "func_%04d" % self.n]
+ self.n += 1
+ return r
+
+ def OnGetLineAttr(self, n):
+ print("getlineattr %d" % n)
+ if n == 1:
+ return [0xFF0000, 0]
+
+for i in xrange(1, 5+1):
+ c = MyChoose2("choose2 - sample %d" % i, i*2)
+ r = c.show()
+ print r
diff --git a/examples/ex_strings.py b/examples/ex_strings.py
index db93b2e..9a0c4d3 100644
--- a/examples/ex_strings.py
+++ b/examples/ex_strings.py
@@ -1,6 +1,9 @@
-import idautils
-
-s = Strings(False)
-s.setup(strtypes=Strings.STR_UNICODE | Strings.STR_C)
-for i in s:
- print "%x: len=%d type=%d -> '%s'" % (i.ea, i.length, i.type, str(i))
+import idautils
+
+s = idautils.Strings(False)
+s.setup(strtypes=Strings.STR_UNICODE | Strings.STR_C)
+for i, v in enumerate(s):
+ if v is None:
+ print("Failed to retrieve string index %d" % i)
+ else:
+ print("%x: len=%d type=%d index=%d-> '%s'" % (v.ea, v.length, v.type, i, str(v)))
diff --git a/examples/ex_uihook.py b/examples/ex_uihook.py
new file mode 100644
index 0000000..1334f02
--- /dev/null
+++ b/examples/ex_uihook.py
@@ -0,0 +1,42 @@
+#---------------------------------------------------------------------
+# UI hook example
+#
+# (c) Hex-Rays
+#
+# Maintained By: IDAPython Team
+#
+#---------------------------------------------------------------------
+
+import idaapi
+
+class MyUiHook(idaapi.UI_Hooks):
+ def __init__(self):
+ idaapi.UI_Hooks.__init__(self)
+ self.cmdname = ""
+
+ def preprocess(self, name):
+ print("IDA preprocessing command: %s" % name)
+ self.cmdname = name
+ return 0
+
+ def postprocess(self):
+ print("IDA finished processing command: %s" % self.cmdname)
+ return 0
+
+
+#---------------------------------------------------------------------
+# Remove an existing hook on second run
+try:
+ ui_hook_stat = "un"
+ print("UI hook: checking for hook...")
+ uihook
+ print("UI hook: unhooking....")
+ uihook.unhook()
+ del uihook
+except:
+ print("UI hook: not installed, installing now....")
+ ui_hook_stat = ""
+ uihook = MyUiHook()
+ uihook.hook()
+
+print("UI hook %sinstalled. Run the script again to %sinstall" % (ui_hook_stat, ui_hook_stat))
diff --git a/python.cpp b/python.cpp
index f4cae77..8ef643e 100644
--- a/python.cpp
+++ b/python.cpp
@@ -49,10 +49,10 @@
#define IDAPYTHON_DISABLE_EXTLANG 4
#define PYTHON_DIR_NAME "python"
#define S_IDAPYTHON "IDAPython"
+#define S_INIT_PY "init.py"
static const char S_IDC_ARGS_VARNAME[] = "ARGV";
static const char S_MAIN[] = "__main__";
static const char S_IDC_RUNPYTHON_STATEMENT[] = "RunPythonStatement";
-static const char S_HOTKEY_RUNSTATEMENT[] = "Ctrl-F3";
static const char S_IDAPYTHON_DATA_NODE[] = "IDAPython_Data";
#ifdef PLUGINFIX
@@ -79,6 +79,7 @@ static bool g_menu_installed = false;
static int g_run_when = -1;
static char g_run_script[QMAXPATH];
static char g_idapython_dir[QMAXPATH];
+static char g_runstmt_hotkey[30] = "Ctrl-F3";
//-------------------------------------------------------------------------
// Prototypes and forward declarations
@@ -124,6 +125,9 @@ static bool box_displayed; // has the wait box been displayed?
static time_t start_time; // the start time of the execution
static int script_timeout = 2;
static bool g_ui_ready = false;
+static bool g_alert_auto_scripts = true;
+static bool g_remove_cwd_sys_path = false;
+
void end_execution();
void begin_execution();
@@ -133,14 +137,15 @@ static int break_check(PyObject *obj, _frame *frame, int what, PyObject *arg)
{
if ( wasBreak() )
{
- /* User pressed Cancel in the waitbox; send KeyboardInterrupt exception */
+ // User pressed Cancel in the waitbox; send KeyboardInterrupt exception
PyErr_SetInterrupt();
}
else if ( !box_displayed && ++ninsns > 10 )
{
- /* We check the timer once every 10 calls */
+ // We check the timer once every 10 calls
ninsns = 0;
- if ( script_timeout != 0 && (time(NULL) - start_time > script_timeout) ) /* Timeout elapsed? */
+ // Timeout disabled or elapsed?
+ if ( script_timeout != 0 && (time(NULL) - start_time > script_timeout) )
{
box_displayed = true;
show_wait_box("Running Python script");
@@ -323,13 +328,13 @@ static void handle_python_error(char *errbuf, size_t errbufsize)
static PyObject *GetMainGlobals()
{
PyObject *module = PyImport_AddModule(S_MAIN);
- if ( module == NULL )
- return NULL;
- return PyModule_GetDict(module);
+ return module == NULL ? NULL : PyModule_GetDict(module);
}
//------------------------------------------------------------------------
-static void PythonEvalOrExec(const char *str, const char *filename = "")
+static void PythonEvalOrExec(
+ const char *str,
+ const char *filename = "")
{
// Compile as an expression
PyCompilerFlags cf = {0};
@@ -345,11 +350,12 @@ static void PythonEvalOrExec(const char *str, const char *filename = "")
}
PyObject *py_globals = GetMainGlobals();
+ PYW_GIL_ENSURE;
PyObject *py_result = PyEval_EvalCode(
- (PyCodeObject *) py_code,
+ (PyCodeObject *) py_code,
py_globals,
py_globals);
-
+ PYW_GIL_RELEASE;
Py_DECREF(py_code);
if ( py_result == NULL || PyErr_Occurred() )
@@ -379,7 +385,13 @@ static error_t idaapi idc_runpythonstatement(idc_value_t *argv, idc_value_t *res
PyErr_Clear();
begin_execution();
- PyObject *result = PyRun_String(argv[0].c_str(), Py_file_input, globals, globals );
+ PYW_GIL_ENSURE;
+ PyObject *result = PyRun_String(
+ argv[0].c_str(),
+ Py_file_input,
+ globals,
+ globals);
+ PYW_GIL_RELEASE;
Py_XDECREF(result);
end_execution();
@@ -401,6 +413,45 @@ static error_t idaapi idc_runpythonstatement(idc_value_t *argv, idc_value_t *res
return eOk;
}
+//--------------------------------------------------------------------------
+const char *idaapi set_python_options(
+ const char *keyword,
+ int value_type,
+ const void *value)
+{
+ do
+ {
+ if ( value_type == IDPOPT_STR )
+ {
+ if ( qstrcmp(keyword, "EXEC_STATEMENT_HOTKEY" ) == 0 )
+ {
+ qstrncpy(g_runstmt_hotkey, (const char *)value, sizeof(g_runstmt_hotkey));
+ break;
+ }
+ }
+ else if ( value_type == IDPOPT_NUM )
+ {
+ if ( qstrcmp(keyword, "SCRIPT_TIMEOUT") == 0 )
+ {
+ script_timeout = int(*(uval_t *)value);
+ break;
+ }
+ else if ( qstrcmp(keyword, "ALERT_AUTO_SCRIPTS") == 0 )
+ {
+ g_alert_auto_scripts = *(uval_t *)value != 0;
+ break;
+ }
+ else if ( qstrcmp(keyword, "REMOVE_CWD_SYS_PATH") == 0 )
+ {
+ g_remove_cwd_sys_path = *(uval_t *)value != 0;
+ break;
+ }
+ }
+ return IDPOPT_BADKEY;
+ } while (false);
+ return IDPOPT_OK;
+}
+
//-------------------------------------------------------------------------
// Check for the presence of a file in IDADIR/python and complain on error
bool CheckScriptFiles()
@@ -408,7 +459,7 @@ bool CheckScriptFiles()
static const char *const script_files[] =
{
S_IDC_MODNAME ".py",
- "init.py",
+ S_INIT_PY,
"idaapi.py",
"idautils.py"
};
@@ -439,9 +490,19 @@ static int PyRunFile(const char *FileName)
return 0;
}
PyErr_Clear();
- PyObject *result = PyRun_File(PyFile_AsFile(PyFileObject), FileName, Py_file_input, globals, globals);
+
+ PYW_GIL_ENSURE;
+ PyObject *result = PyRun_File(
+ PyFile_AsFile(PyFileObject),
+ FileName,
+ Py_file_input,
+ globals,
+ globals);
+ PYW_GIL_RELEASE;
+
Py_XDECREF(PyFileObject);
Py_XDECREF(result);
+
return result != NULL && !PyErr_Occurred();
}
@@ -461,7 +522,7 @@ void IDAPython_RunStatement(void)
if ( history.getblob(statement, &statement_size, 0, 'A') == NULL )
statement[0] = '\0';
- if ( asktext(sizeof(statement), statement, statement, "Enter Python expressions") != NULL )
+ if ( asktext(sizeof(statement), statement, statement, "ACCEPT TABS\nEnter Python expressions") != NULL )
{
begin_execution();
PyRun_SimpleString(statement);
@@ -489,7 +550,13 @@ static bool IDAPython_ExecFile(const char *FileName, char *errbuf, size_t errbuf
strrpl(script, '\\', '//');
PyObject *py_script = PyString_FromString(script);
- PyObject *py_ret = PyObject_CallFunctionObjArgs(py_execscript, py_script, GetMainGlobals(), NULL);
+ PYW_GIL_ENSURE;
+ PyObject *py_ret = PyObject_CallFunctionObjArgs(
+ py_execscript,
+ py_script,
+ GetMainGlobals(),
+ NULL);
+ PYW_GIL_RELEASE;
Py_DECREF(py_script);
Py_DECREF(py_execscript);
@@ -516,7 +583,7 @@ static bool IDAPython_ExecFile(const char *FileName, char *errbuf, size_t errbuf
}
// Cannot be otherwise!
else
- INTERR();
+ INTERR(30154);
Py_XDECREF(py_ret);
return ok;
@@ -527,10 +594,12 @@ static bool IDAPython_ExecFile(const char *FileName, char *errbuf, size_t errbuf
static bool RunScript(const char *script)
{
begin_execution();
+
char errbuf[MAXSTR];
bool ok = IDAPython_ExecFile(script, errbuf, sizeof(errbuf));
if ( !ok )
warning("IDAPython: error executing '%s':\n%s", script, errbuf);
+
end_execution();
return ok;
}
@@ -612,7 +681,6 @@ bool idaapi IDAPython_extlang_compile(
size_t errbufsize)
{
PyObject *globals = GetMainGlobals();
- QASSERT(globals != NULL);
PyCodeObject *code = (PyCodeObject *)Py_CompileString(expr, "", Py_eval_input);
if ( code == NULL )
@@ -621,11 +689,11 @@ bool idaapi IDAPython_extlang_compile(
return false;
}
- // set the desired function name
+ // Set the desired function name
Py_XDECREF(code->co_name);
code->co_name = PyString_FromString(name);
- // create a function out of code
+ // Create a function out of code
PyObject *func = PyFunction_New((PyObject *)code, globals);
if ( func == NULL )
@@ -672,16 +740,18 @@ bool idaapi IDAPython_extlang_run(
if ( imported_module )
{
+ PYW_GIL_ENSURE;
module = PyImport_ImportModule(modname);
+ PYW_GIL_RELEASE;
}
else
{
module = PyImport_AddModule(S_MAIN);
- QASSERT(module != NULL);
+ QASSERT(30156, module != NULL);
}
PyObject *globals = PyModule_GetDict(module);
- QASSERT(globals != NULL);
+ QASSERT(30157, globals != NULL);
PyObject *func = PyDict_GetItemString(globals, funcname);
if ( func == NULL )
@@ -692,12 +762,13 @@ bool idaapi IDAPython_extlang_run(
}
PyCodeObject *code = (PyCodeObject *) PyFunction_GetCode(func);
+ PYW_GIL_ENSURE;
PyObject *pres = PyEval_EvalCodeEx(
code,
- globals, NULL,
+ globals, NULL,
&pargs[0], nargs,
NULL, 0, NULL, 0, NULL);
-
+ PYW_GIL_RELEASE;
ok = return_python_result(result, pres, errbuf, errbufsize);
} while ( false );
@@ -705,7 +776,7 @@ bool idaapi IDAPython_extlang_run(
if ( imported_module )
Py_XDECREF(module);
-
+
return ok;
}
@@ -766,7 +837,9 @@ bool idaapi IDAPython_extlang_create_object(
ok = false;
// Call the constructor
+ PYW_GIL_ENSURE;
PyObject *py_res = PyObject_CallObject(py_cls, pargs.empty() ? NULL : pargs[0]);
+ PYW_GIL_RELEASE;
ok = return_python_result(result, py_res, errbuf, errbufsize);
} while ( false );
@@ -944,7 +1017,9 @@ bool idaapi IDAPython_extlang_calcexpr(
return false;
begin_execution();
+ PYW_GIL_ENSURE;
PyObject *result = PyRun_String(expr, Py_eval_input, globals, globals);
+ PYW_GIL_RELEASE;
end_execution();
return return_python_result(rv, result, errbuf, errbufsize);
@@ -998,7 +1073,9 @@ bool idaapi IDAPython_extlang_call_method(
if ( !ok )
break;
+ PYW_GIL_ENSURE;
PyObject *py_res = PyObject_CallObject(py_method, pargs.empty() ? NULL : pargs[0]);
+ PYW_GIL_RELEASE;
ok = return_python_result(result, py_res, errbuf, errbufsize);
} while ( false );
@@ -1030,6 +1107,7 @@ extlang_t extlang_python =
IDAPython_extlang_call_method
};
+//-------------------------------------------------------------------------
void enable_extlang_python(bool enable)
{
if ( enable )
@@ -1042,7 +1120,7 @@ void enable_extlang_python(bool enable)
// Execute a line in the Python CLI
bool idaapi IDAPython_cli_execute_line(const char *line)
{
- // do not process empty lines
+ // Do not process empty lines
if ( line[0] == '\0' )
return true;
@@ -1052,11 +1130,11 @@ bool idaapi IDAPython_cli_execute_line(const char *line)
else
last_line += 1;
- // skip empty lines
+ // Skip empty lines
if ( last_line[0] != '\0' )
{
- // line ends with ":" or begins with a space character?
- bool more = last_line[qstrlen(last_line)-1] == ':' || isspace(last_line[0]);
+ // Line ends with ":" or begins with a space character?
+ bool more = last_line[qstrlen(last_line)-1] == ':' || qisspace(last_line[0]);
if ( more )
return false;
}
@@ -1080,7 +1158,9 @@ bool idaapi IDAPYthon_cli_complete_line(
if ( py_complete == NULL )
return false;
+ PYW_GIL_ENSURE;
PyObject *py_ret = PyObject_CallFunction(py_complete, "sisi", prefix, n, line, x);
+ PYW_GIL_RELEASE;
Py_DECREF(py_complete);
@@ -1124,7 +1204,9 @@ void enable_python_cli(bool enable)
// Prints the IDAPython copyright banner
void py_print_banner()
{
+ PYW_GIL_ENSURE;
PyRun_SimpleString("print_banner()");
+ PYW_GIL_RELEASE;
}
//-------------------------------------------------------------------------
@@ -1136,8 +1218,11 @@ static void install_python_menus()
// Add menu items for all the functions
// Note: Different paths are used for the GUI version
- add_menu_item("File/IDC command...", "P~y~thon command...",
- S_HOTKEY_RUNSTATEMENT, SETMENU_APP,
+ add_menu_item(
+ "File/IDC command...",
+ "P~y~thon command...",
+ g_runstmt_hotkey,
+ SETMENU_APP,
IDAPython_Menu_Callback,
(void *)IDAPYTHON_RUNSTATEMENT);
@@ -1277,6 +1362,19 @@ bool IDAPython_Init(void)
}
#endif
+ // Read configuration value
+ read_user_config_file("python.cfg", set_python_options, NULL);
+ if ( g_alert_auto_scripts )
+ {
+ const char *autofn = pywraps_check_autoscripts();
+ if ( autofn != NULL
+ && askyn_c(0, "HIDECANCEL\nTITLE IDAPython\nThe script '%s' was found in the current directory and will be automatically executed by Python.\n\n"
+ "Do you want to continue loading IDAPython?", autofn) == 0 )
+ {
+ return false;
+ }
+ }
+
// Start the interpreter
Py_Initialize();
if ( !Py_IsInitialized() )
@@ -1285,16 +1383,22 @@ bool IDAPython_Init(void)
return false;
}
+ // Enable multi-threading support
+ if ( !PyEval_ThreadsInitialized() )
+ PyEval_InitThreads();
+
// Init the SWIG wrapper
init_idaapi();
// Set IDAPYTHON_VERSION in Python
- qsnprintf(tmp, sizeof(tmp), "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)", \
+ qsnprintf(tmp, sizeof(tmp), "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)\n"
+ "IDAPYTHON_REMOVE_CWD_SYS_PATH = %s\n",
VER_MAJOR,
VER_MINOR,
VER_PATCH,
VER_STATUS,
- VER_SERIAL);
+ VER_SERIAL,
+ g_remove_cwd_sys_path ? "True" : "False");
PyRun_SimpleString(tmp);
// Install extlang. Needs to be done before running init.py
@@ -1302,11 +1406,11 @@ bool IDAPython_Init(void)
install_extlang(&extlang_python);
// Execute init.py (for Python side initialization)
- qmakepath(tmp, MAXSTR, g_idapython_dir, "init.py", NULL);
+ qmakepath(tmp, MAXSTR, g_idapython_dir, S_INIT_PY, NULL);
if ( !PyRunFile(tmp) )
{
handle_python_error(tmp, sizeof(tmp));
- warning("IDAPython: error executing init.py:\n%s", tmp);
+ warning("IDAPython: error executing " S_INIT_PY ":\n%s", tmp);
return false;
}
@@ -1357,18 +1461,18 @@ void IDAPython_Term(void)
del_menu_item("File/Python command...");
g_menu_installed = false;
- // Remove the CLI
- enable_python_cli(false);
-
- // Remove the extlang
- remove_extlang(&extlang_python);
-
// Notify about IDA closing
pywraps_nw_notify(NW_TERMIDA_SLOT);
// De-init notify_when
pywraps_nw_term();
+ // Remove the CLI
+ enable_python_cli(false);
+
+ // Remove the extlang
+ remove_extlang(&extlang_python);
+
// De-init pywraps
deinit_pywraps();
@@ -1446,5 +1550,5 @@ plugin_t PLUGIN =
// the preferred short name of the plugin
S_IDAPYTHON,
// the preferred hotkey to run the plugin
- S_HOTKEY_RUNSTATEMENT
+ NULL
};
diff --git a/python/idautils.py b/python/idautils.py
index 44abcb7..6c3d768 100644
--- a/python/idautils.py
+++ b/python/idautils.py
@@ -1,662 +1,711 @@
-#---------------------------------------------------------------------
-# IDAPython - Python plugin for Interactive Disassembler Pro
-#
-# Copyright (c) 2004-2010 Gergely Erdelyi
-#
-# All rights reserved.
-#
-# For detailed copyright information see the file COPYING in
-# the root of the distribution archive.
-#---------------------------------------------------------------------
-"""
-idautils.py - High level utility functions for IDA
-"""
-import idaapi
-import idc
-import types
-import os
-
-
-def refs(ea, funcfirst, funcnext):
- """
- Generic reference collector - INTERNAL USE ONLY.
- """
- ref = funcfirst(ea)
- while ref != idaapi.BADADDR:
- yield ref
- ref = funcnext(ea, ref)
-
-
-def CodeRefsTo(ea, flow):
- """
- Get a list of code references to 'ea'
-
- @param ea: Target address
- @param flow: Follow normal code flow or not
- @type flow: Boolean (0/1, False/True)
-
- @return: list of references (may be empty list)
-
- Example::
-
- for ref in CodeRefsTo(ScreenEA(), 1):
- print ref
- """
- if flow == 1:
- return refs(ea, idaapi.get_first_cref_to, idaapi.get_next_cref_to)
- else:
- return refs(ea, idaapi.get_first_fcref_to, idaapi.get_next_fcref_to)
-
-
-def CodeRefsFrom(ea, flow):
- """
- Get a list of code references from 'ea'
-
- @param ea: Target address
- @param flow: Follow normal code flow or not
- @type flow: Boolean (0/1, False/True)
-
- @return: list of references (may be empty list)
-
- Example::
-
- for ref in CodeRefsFrom(ScreenEA(), 1):
- print ref
- """
- if flow == 1:
- return refs(ea, idaapi.get_first_cref_from, idaapi.get_next_cref_from)
- else:
- return refs(ea, idaapi.get_first_fcref_from, idaapi.get_next_fcref_from)
-
-
-def DataRefsTo(ea):
- """
- Get a list of data references to 'ea'
-
- @param ea: Target address
-
- @return: list of references (may be empty list)
-
- Example::
-
- for ref in DataRefsTo(ScreenEA()):
- print ref
- """
- return refs(ea, idaapi.get_first_dref_to, idaapi.get_next_dref_to)
-
-
-def DataRefsFrom(ea):
- """
- Get a list of data references from 'ea'
-
- @param ea: Target address
-
- @return: list of references (may be empty list)
-
- Example::
-
- for ref in DataRefsFrom(ScreenEA()):
- print ref
- """
- return refs(ea, idaapi.get_first_dref_from, idaapi.get_next_dref_from)
-
-
-def XrefTypeName(typecode):
- """
- Convert cross-reference type codes to readable names
-
- @param typecode: cross-reference type code
- """
- ref_types = {
- 0 : 'Data_Unknown',
- 1 : 'Data_Offset',
- 2 : 'Data_Write',
- 3 : 'Data_Read',
- 4 : 'Data_Text',
- 5 : 'Data_Informational',
- 16 : 'Code_Far_Call',
- 17 : 'Code_Near_Call',
- 18 : 'Code_Far_Jump',
- 19 : 'Code_Near_Jump',
- 20 : 'Code_User',
- 21 : 'Ordinary_Flow'
- }
- assert typecode in ref_types, "unknown reference type %d" % typecode
- return ref_types[typecode]
-
-
-def _copy_xref(xref):
- """ Make a private copy of the xref class to preserve its contents """
- class _xref(object):
- pass
-
- xr = _xref()
- for attr in [ 'frm', 'to', 'iscode', 'type', 'user' ]:
- setattr(xr, attr, getattr(xref, attr))
- return xr
-
-
-def XrefsFrom(ea, flags=0):
- """
- Return all references from address 'ea'
-
- @param ea: Reference address
- @param flags: any of idaapi.XREF_* flags
-
- Example::
- for xref in XrefsFrom(here(), 0):
- print xref.type, XrefTypeName(xref.type), \
- 'from', hex(xref.frm), 'to', hex(xref.to)
- """
- xref = idaapi.xrefblk_t()
- if xref.first_from(ea, flags):
- yield _copy_xref(xref)
- while xref.next_from():
- yield _copy_xref(xref)
-
-
-def XrefsTo(ea, flags=0):
- """
- Return all references to address 'ea'
-
- @param ea: Reference address
- @param flags: any of idaapi.XREF_* flags
-
- Example::
- for xref in XrefsTo(here(), 0):
- print xref.type, XrefTypeName(xref.type), \
- 'from', hex(xref.frm), 'to', hex(xref.to)
- """
- xref = idaapi.xrefblk_t()
- if xref.first_to(ea, flags):
- yield _copy_xref(xref)
- while xref.next_to():
- yield _copy_xref(xref)
-
-
-def Threads():
- """Returns all thread IDs"""
- for i in xrange(0, idc.GetThreadQty()):
- yield idc.GetThreadId(i)
-
-
-def Heads(start=None, end=None):
- """
- Get a list of heads (instructions or data)
-
- @param start: start address (default: inf.minEA)
- @param end: end address (default: inf.maxEA)
-
- @return: list of heads between start and end
- """
- if not start: start = idaapi.cvar.inf.minEA
- if not end: end = idaapi.cvar.inf.maxEA
-
- ea = start
- if not idc.isHead(idc.GetFlags(ea)):
- ea = idaapi.next_head(ea, end)
- while ea != idaapi.BADADDR:
- yield ea
- ea = idaapi.next_head(ea, end)
-
-
-def Functions(start=None, end=None):
- """
- Get a list of functions
-
- @param start: start address (default: inf.minEA)
- @param end: end address (default: inf.maxEA)
-
- @return: list of heads between start and end
-
- @note: The last function that starts before 'end' is included even
- if it extends beyond 'end'. Any function that has its chunks scattered
- in multiple segments will be reported multiple times, once in each segment
- as they are listed.
- """
- if not start: start = idaapi.cvar.inf.minEA
- if not end: end = idaapi.cvar.inf.maxEA
-
- func = idaapi.get_func(start)
- if not func:
- func = idaapi.get_next_func(start)
- while func and func.startEA < end:
- startea = func.startEA
- yield startea
- func = idaapi.get_next_func(startea)
-
-
-def Chunks(start):
- """
- Get a list of function chunks
-
- @param start: address of the function
-
- @return: list of funcion chunks (tuples of the form (start_ea, end_ea))
- belonging to the function
- """
- func_iter = idaapi.func_tail_iterator_t( idaapi.get_func( start ) )
- status = func_iter.main()
- while status:
- chunk = func_iter.chunk()
- yield (chunk.startEA, chunk.endEA)
- status = func_iter.next()
-
-
-def Modules():
- """
- Returns a list of module objects with name,size,base and the rebase_to attributes
- """
- mod = idaapi.module_info_t()
- result = idaapi.get_first_module(mod)
- while result:
- yield idaapi.object_t(name=mod.name, size=mod.size, base=mod.base, rebase_to=mod.rebase_to)
- result = idaapi.get_next_module(mod)
-
-
-def Names():
- """
- Returns a list of names
-
- @return: List of tuples (ea, name)
- """
- for i in xrange(idaapi.get_nlist_size()):
- ea = idaapi.get_nlist_ea(i)
- name = idaapi.get_nlist_name(i)
- yield (ea, name)
-
-
-def Segments():
- """
- Get list of segments (sections) in the binary image
-
- @return: List of segment start addresses.
- """
- for n in xrange(idaapi.get_segm_qty()):
- seg = idaapi.getnseg(n)
- if seg:
- yield seg.startEA
-
-
-def Entries():
- """
- Returns a list of entry points
-
- @return: List of tuples (index, ordinal, ea, name)
- """
- n = idaapi.get_entry_qty()
- for i in xrange(0, n):
- ordinal = idaapi.get_entry_ordinal(i)
- ea = idaapi.get_entry(ordinal)
- name = idaapi.get_entry_name(ordinal)
- yield (i, ordinal, ea, name)
-
-
-def FuncItems(start):
- """
- Get a list of function items
-
- @param start: address of the function
-
- @return: ea of each item in the function
- """
- func = idaapi.get_func(start)
- if not func:
- return
- fii = idaapi.func_item_iterator_t()
- ok = fii.set(func)
- while ok:
- yield fii.current()
- ok = fii.next_code()
-
-
-def DecodeInstruction(ea):
- """
- Decodes an instruction and returns an insn_t like class
-
- @param ea: address to decode
- @return: None or a new insn_t instance
- """
- inslen = idaapi.decode_insn(ea)
- if inslen == 0:
- return None
-
- return idaapi.cmd.copy()
-
-
-def GetDataList(ea, count, itemsize=1):
- """
- Get data list - INTERNAL USE ONLY
- """
- if itemsize == 1:
- getdata = idaapi.get_byte
- elif itemsize == 2:
- getdata = idaapi.get_word
- elif itemsize == 4:
- getdata = idaapi.get_long
- elif itemsize == 8:
- getdata = idaapi.get_qword
- else:
- raise ValueError, "Invalid data size! Must be 1, 2, 4 or 8"
-
- endea = ea + itemsize * count
- curea = ea
- while curea < endea:
- yield getdata(curea)
- curea += itemsize
-
-
-def PutDataList(ea, datalist, itemsize=1):
- """
- Put data list - INTERNAL USE ONLY
- """
- putdata = None
-
- if itemsize == 1:
- putdata = idaapi.patch_byte
- if itemsize == 2:
- putdata = idaapi.patch_word
- if itemsize == 4:
- putdata = idaapi.patch_long
-
- assert putdata, "Invalid data size! Must be 1, 2 or 4"
-
- for val in datalist:
- putdata(ea, val)
- ea = ea + itemsize
-
-
-def MapDataList(ea, length, func, wordsize=1):
- """
- Map through a list of data words in the database
-
- @param ea: start address
- @param length: number of words to map
- @param func: mapping function
- @param wordsize: size of words to map [default: 1 byte]
-
- @return: None
- """
- PutDataList(ea, map(func, GetDataList(ea, length, wordsize)), wordsize)
-
-
-def GetInputFileMD5():
- """
- Return the MD5 hash of the input binary file
-
- @return: MD5 string or None on error
- """
- return idc.GetInputMD5()
-
-
-class Strings(object):
- """
- Returns the string list.
-
- Example:
- s = Strings()
-
- for i in s:
- print "%x: len=%d type=%d -> '%s'" % (i.ea, i.length, i.type, str(i))
-
- """
- class StringItem(object):
- """
- Class representing each string item.
- """
- def __init__(self, si):
- self.ea = si.ea
- """String ea"""
- self.type = si.type
- """string type (ASCSTR_xxxxx)"""
- self.length = si.length
- """string length"""
-
- def __str__(self):
- return idc.GetString(self.ea, self.length, self.type)
-
- STR_C = 0x0001
- """C-style ASCII string"""
- STR_PASCAL = 0x0002
- """Pascal-style ASCII string (length byte)"""
- STR_LEN2 = 0x0004
- """Pascal-style, length is 2 bytes"""
- STR_UNICODE = 0x0008
- """Unicode string"""
- STR_LEN4 = 0x0010
- """Pascal-style, length is 4 bytes"""
- STR_ULEN2 = 0x0020
- """Pascal-style Unicode, length is 2 bytes"""
- STR_ULEN4 = 0x0040
- """Pascal-style Unicode, length is 4 bytes"""
-
- def clear_cache(self):
- """Clears the strings list cache"""
- self.refresh(0, 0) # when ea1=ea2 the kernel will clear the cache
-
- def __init__(self, default_setup = True):
- """
- Initializes the Strings enumeration helper class
-
- @param default_setup: Set to True to use default setup (C strings, min len 5, ...)
- """
- self.size = 0
- if default_setup:
- self.setup()
-
- self._si = idaapi.string_info_t()
-
- def refresh(self, ea1=None, ea2=None):
- """Refreshes the strings list"""
- if ea1 is None:
- ea1 = idaapi.cvar.inf.minEA
- if ea2 is None:
- ea2 = idaapi.cvar.inf.maxEA
-
- idaapi.refresh_strlist(ea1, ea2)
- self.size = idaapi.get_strlist_qty()
-
- def setup(self,
- strtypes = STR_C,
- minlen = 5,
- only_7bit = True,
- ignore_instructions = False,
- ea1 = None,
- ea2 = None,
- display_only_existing_strings = False):
-
- if ea1 is None:
- ea1 = idaapi.cvar.inf.minEA
-
- if ea2 is None:
- ea2 = idaapi.cvar.inf.maxEA
-
- t = idaapi.strwinsetup_t()
- t.strtypes = strtypes
- t.minlen = minlen
- t.only_7bit = only_7bit
- t.ea1 = ea1
- t.ea2 = ea2
- t.display_only_existing_strings = display_only_existing_strings
- idaapi.set_strlist_options(t)
-
- # Automatically refreshes
- self.refresh()
-
- def __getitem__(self, index):
- """Returns a string item or None"""
- if index >= self.size:
- raise StopIteration
-
- if idaapi.get_strlist_item(index, self._si):
- return Strings.StringItem(self._si)
-
- return None
-
-# -----------------------------------------------------------------------
-def GetIdbDir():
- """
- Get IDB directory
-
- This function returns directory path of the current IDB database
- """
- return os.path.dirname(idaapi.cvar.database_idb) + os.sep
-
-# -----------------------------------------------------------------------
-def GetRegisterList():
- """Returns the register list"""
- return idaapi.ph_get_regnames()
-
-# -----------------------------------------------------------------------
-def GetInstructionList():
- """Returns the instruction list of the current processor module"""
- return [i[0] for i in idaapi.ph_get_instruc() if i[0]]
-
-# -----------------------------------------------------------------------
-def _Assemble(ea, line):
- """
- Please refer to Assemble() - INTERNAL USE ONLY
- """
- if type(line) == types.StringType:
- lines = [line]
- else:
- lines = line
- ret = []
- for line in lines:
- seg = idaapi.getseg(ea)
- if not seg:
- return (False, "No segment at ea")
- ip = ea - (idaapi.ask_selector(seg.sel) << 4)
- buf = idaapi.AssembleLine(ea, seg.sel, ip, seg.bitness, line)
- if not buf:
- return (False, "Assembler failed: " + line)
- ea += len(buf)
- ret.append(buf)
-
- if len(ret) == 1:
- ret = ret[0]
- return (True, ret)
-
-
-def Assemble(ea, line):
- """
- Assembles one or more lines (does not display an message dialogs)
- If line is a list then this function will attempt to assemble all the lines
- This function will turn on batch mode temporarily so that no messages are displayed on the screen
-
- @param ea: start address
- @return: (False, "Error message") or (True, asm_buf) or (True, [asm_buf1, asm_buf2, asm_buf3])
- """
- old_batch = idc.Batch(1)
- ret = _Assemble(ea, line)
- idc.Batch(old_batch)
- return ret
-
-def _copy_obj(src, dest, skip_list = None):
- """
- Copy non private/non callable attributes from a class instance to another
- @param src: Source class to copy from
- @param dest: If it is a string then it designates the new class type that will be created and copied to.
- Otherwise dest should be an instance of another class
- @return: A new instance or "dest"
- """
- if type(dest) == types.StringType:
- # instantiate a new destination class of the specified type name?
- dest = new.classobj(dest, (), {})
- for x in dir(src):
- # skip special and private fields
- if x.startswith("__") and x.endswith("__"):
- continue
- # skip items in the skip list
- if skip_list and x in skip_list:
- continue
- t = getattr(src, x)
- # skip callable
- if callable(t):
- continue
- setattr(dest, x, t)
- return dest
-
-# -----------------------------------------------------------------------
-class _reg_dtyp_t(object):
- """
- INTERNAL
- This class describes a register's number and dtyp.
- The equal operator is overloaded so that two instances can be tested for equality
- """
- def __init__(self, reg, dtyp):
- self.reg = reg
- self.dtyp = dtyp
-
- def __eq__(self, other):
- return (self.reg == other.reg) and (self.dtyp == other.dtyp)
-
-# -----------------------------------------------------------------------
-class _procregs(object):
- """Utility class allowing the users to identify registers in a decoded instruction"""
- def __getattr__(self, attr):
- ri = idaapi.reg_info_t()
- if not idaapi.parse_reg_name(attr, ri):
- raise AttributeError()
- r = _reg_dtyp_t(ri.reg, ord(idaapi.get_dtyp_by_size(ri.size)))
- self.__dict__[attr] = r
- return r
-
- def __setattr__(self, attr, value):
- raise AttributeError(attr)
-
-# -----------------------------------------------------------------------
-class _cpu(object):
- "Simple wrapper around GetRegValue/SetRegValue"
- def __getattr__(self, name):
- #print "cpu.get(%s)"%name
- return idc.GetRegValue(name)
-
- def __setattr__(self, name, value):
- #print "cpu.set(%s)"%name
- return idc.SetRegValue(value, name)
-
-# -----------------------------------------------------------------------
-class peutils_t(object):
- """
- PE utility class. Retrieves PE information from the database.
-
- Constants from pe.h
- """
- PE_NODE = "$ PE header" # netnode name for PE header
- PE_ALT_DBG_FPOS = idaapi.BADADDR & -1 # altval() -> translated fpos of debuginfo
- PE_ALT_IMAGEBASE = idaapi.BADADDR & -2 # altval() -> loading address (usually pe.imagebase)
- PE_ALT_PEHDR_OFF = idaapi.BADADDR & -3 # altval() -> offset of PE header
- PE_ALT_NEFLAGS = idaapi.BADADDR & -4 # altval() -> neflags
- PE_ALT_TDS_LOADED = idaapi.BADADDR & -5 # altval() -> tds already loaded(1) or invalid(-1)
- PE_ALT_PSXDLL = idaapi.BADADDR & -6 # altval() -> if POSIX(x86) imports from PSXDLL netnode
-
- def __init__(self):
- self.__penode = idaapi.netnode()
- self.__penode.create(peutils_t.PE_NODE)
-
- imagebase = property(
- lambda self: self.__penode.altval(peutils_t.PE_ALT_IMAGEBASE)
- )
-
- header = property(
- lambda self: self.__penode.altval(peutils_t.PE_ALT_PEHDR_OFF)
- )
-
- def __str__(self):
- return "peutils_t(imagebase=%s, header=%s)" % (hex(self.imagebase), hex(self.header))
-
-# -----------------------------------------------------------------------
-cpu = _cpu()
-"""This is a special class instance used to access the registers as if they were attributes of this object.
-For example to access the EAX register:
- print "%x" % cpu.Eax
-"""
-
-procregs = _procregs()
-"""This object is used to access the processor registers. It is useful when decoding instructions and you want to see which instruction is which.
-For example:
- x = idautils.DecodeInstruction(here())
- if x[0] == procregs.Esp:
- print "This operand is the register ESP
+#---------------------------------------------------------------------
+# IDAPython - Python plugin for Interactive Disassembler Pro
+#
+# Copyright (c) 2004-2010 Gergely Erdelyi
+#
+# All rights reserved.
+#
+# For detailed copyright information see the file COPYING in
+# the root of the distribution archive.
+#---------------------------------------------------------------------
+"""
+idautils.py - High level utility functions for IDA
+"""
+import idaapi
+import idc
+import types
+import os
+
+
+def refs(ea, funcfirst, funcnext):
+ """
+ Generic reference collector - INTERNAL USE ONLY.
+ """
+ ref = funcfirst(ea)
+ while ref != idaapi.BADADDR:
+ yield ref
+ ref = funcnext(ea, ref)
+
+
+def CodeRefsTo(ea, flow):
+ """
+ Get a list of code references to 'ea'
+
+ @param ea: Target address
+ @param flow: Follow normal code flow or not
+ @type flow: Boolean (0/1, False/True)
+
+ @return: list of references (may be empty list)
+
+ Example::
+
+ for ref in CodeRefsTo(ScreenEA(), 1):
+ print ref
+ """
+ if flow == 1:
+ return refs(ea, idaapi.get_first_cref_to, idaapi.get_next_cref_to)
+ else:
+ return refs(ea, idaapi.get_first_fcref_to, idaapi.get_next_fcref_to)
+
+
+def CodeRefsFrom(ea, flow):
+ """
+ Get a list of code references from 'ea'
+
+ @param ea: Target address
+ @param flow: Follow normal code flow or not
+ @type flow: Boolean (0/1, False/True)
+
+ @return: list of references (may be empty list)
+
+ Example::
+
+ for ref in CodeRefsFrom(ScreenEA(), 1):
+ print ref
+ """
+ if flow == 1:
+ return refs(ea, idaapi.get_first_cref_from, idaapi.get_next_cref_from)
+ else:
+ return refs(ea, idaapi.get_first_fcref_from, idaapi.get_next_fcref_from)
+
+
+def DataRefsTo(ea):
+ """
+ Get a list of data references to 'ea'
+
+ @param ea: Target address
+
+ @return: list of references (may be empty list)
+
+ Example::
+
+ for ref in DataRefsTo(ScreenEA()):
+ print ref
+ """
+ return refs(ea, idaapi.get_first_dref_to, idaapi.get_next_dref_to)
+
+
+def DataRefsFrom(ea):
+ """
+ Get a list of data references from 'ea'
+
+ @param ea: Target address
+
+ @return: list of references (may be empty list)
+
+ Example::
+
+ for ref in DataRefsFrom(ScreenEA()):
+ print ref
+ """
+ return refs(ea, idaapi.get_first_dref_from, idaapi.get_next_dref_from)
+
+
+def XrefTypeName(typecode):
+ """
+ Convert cross-reference type codes to readable names
+
+ @param typecode: cross-reference type code
+ """
+ ref_types = {
+ 0 : 'Data_Unknown',
+ 1 : 'Data_Offset',
+ 2 : 'Data_Write',
+ 3 : 'Data_Read',
+ 4 : 'Data_Text',
+ 5 : 'Data_Informational',
+ 16 : 'Code_Far_Call',
+ 17 : 'Code_Near_Call',
+ 18 : 'Code_Far_Jump',
+ 19 : 'Code_Near_Jump',
+ 20 : 'Code_User',
+ 21 : 'Ordinary_Flow'
+ }
+ assert typecode in ref_types, "unknown reference type %d" % typecode
+ return ref_types[typecode]
+
+
+def _copy_xref(xref):
+ """ Make a private copy of the xref class to preserve its contents """
+ class _xref(object):
+ pass
+
+ xr = _xref()
+ for attr in [ 'frm', 'to', 'iscode', 'type', 'user' ]:
+ setattr(xr, attr, getattr(xref, attr))
+ return xr
+
+
+def XrefsFrom(ea, flags=0):
+ """
+ Return all references from address 'ea'
+
+ @param ea: Reference address
+ @param flags: any of idaapi.XREF_* flags
+
+ Example::
+ for xref in XrefsFrom(here(), 0):
+ print xref.type, XrefTypeName(xref.type), \
+ 'from', hex(xref.frm), 'to', hex(xref.to)
+ """
+ xref = idaapi.xrefblk_t()
+ if xref.first_from(ea, flags):
+ yield _copy_xref(xref)
+ while xref.next_from():
+ yield _copy_xref(xref)
+
+
+def XrefsTo(ea, flags=0):
+ """
+ Return all references to address 'ea'
+
+ @param ea: Reference address
+ @param flags: any of idaapi.XREF_* flags
+
+ Example::
+ for xref in XrefsTo(here(), 0):
+ print xref.type, XrefTypeName(xref.type), \
+ 'from', hex(xref.frm), 'to', hex(xref.to)
+ """
+ xref = idaapi.xrefblk_t()
+ if xref.first_to(ea, flags):
+ yield _copy_xref(xref)
+ while xref.next_to():
+ yield _copy_xref(xref)
+
+
+def Threads():
+ """Returns all thread IDs"""
+ for i in xrange(0, idc.GetThreadQty()):
+ yield idc.GetThreadId(i)
+
+
+def Heads(start=None, end=None):
+ """
+ Get a list of heads (instructions or data)
+
+ @param start: start address (default: inf.minEA)
+ @param end: end address (default: inf.maxEA)
+
+ @return: list of heads between start and end
+ """
+ if not start: start = idaapi.cvar.inf.minEA
+ if not end: end = idaapi.cvar.inf.maxEA
+
+ ea = start
+ if not idc.isHead(idc.GetFlags(ea)):
+ ea = idaapi.next_head(ea, end)
+ while ea != idaapi.BADADDR:
+ yield ea
+ ea = idaapi.next_head(ea, end)
+
+
+def Functions(start=None, end=None):
+ """
+ Get a list of functions
+
+ @param start: start address (default: inf.minEA)
+ @param end: end address (default: inf.maxEA)
+
+ @return: list of heads between start and end
+
+ @note: The last function that starts before 'end' is included even
+ if it extends beyond 'end'. Any function that has its chunks scattered
+ in multiple segments will be reported multiple times, once in each segment
+ as they are listed.
+ """
+ if not start: start = idaapi.cvar.inf.minEA
+ if not end: end = idaapi.cvar.inf.maxEA
+
+ func = idaapi.get_func(start)
+ if not func:
+ func = idaapi.get_next_func(start)
+ while func and func.startEA < end:
+ startea = func.startEA
+ yield startea
+ func = idaapi.get_next_func(startea)
+
+
+def Chunks(start):
+ """
+ Get a list of function chunks
+
+ @param start: address of the function
+
+ @return: list of funcion chunks (tuples of the form (start_ea, end_ea))
+ belonging to the function
+ """
+ func_iter = idaapi.func_tail_iterator_t( idaapi.get_func( start ) )
+ status = func_iter.main()
+ while status:
+ chunk = func_iter.chunk()
+ yield (chunk.startEA, chunk.endEA)
+ status = func_iter.next()
+
+
+def Modules():
+ """
+ Returns a list of module objects with name,size,base and the rebase_to attributes
+ """
+ mod = idaapi.module_info_t()
+ result = idaapi.get_first_module(mod)
+ while result:
+ yield idaapi.object_t(name=mod.name, size=mod.size, base=mod.base, rebase_to=mod.rebase_to)
+ result = idaapi.get_next_module(mod)
+
+
+def Names():
+ """
+ Returns a list of names
+
+ @return: List of tuples (ea, name)
+ """
+ for i in xrange(idaapi.get_nlist_size()):
+ ea = idaapi.get_nlist_ea(i)
+ name = idaapi.get_nlist_name(i)
+ yield (ea, name)
+
+
+def Segments():
+ """
+ Get list of segments (sections) in the binary image
+
+ @return: List of segment start addresses.
+ """
+ for n in xrange(idaapi.get_segm_qty()):
+ seg = idaapi.getnseg(n)
+ if seg:
+ yield seg.startEA
+
+
+def Entries():
+ """
+ Returns a list of entry points
+
+ @return: List of tuples (index, ordinal, ea, name)
+ """
+ n = idaapi.get_entry_qty()
+ for i in xrange(0, n):
+ ordinal = idaapi.get_entry_ordinal(i)
+ ea = idaapi.get_entry(ordinal)
+ name = idaapi.get_entry_name(ordinal)
+ yield (i, ordinal, ea, name)
+
+
+def FuncItems(start):
+ """
+ Get a list of function items
+
+ @param start: address of the function
+
+ @return: ea of each item in the function
+ """
+ func = idaapi.get_func(start)
+ if not func:
+ return
+ fii = idaapi.func_item_iterator_t()
+ ok = fii.set(func)
+ while ok:
+ yield fii.current()
+ ok = fii.next_code()
+
+
+
+def DecodePrecedingInstruction(ea):
+ """
+ Decode preceding instruction in the execution flow.
+
+ @param ea: address to decode
+ @return: (None or the decode instruction, farref)
+ farref will contain 'true' if followed an xref, false otherwise
+ """
+ prev_addr, farref = idaapi.decode_preceding_insn(ea)
+ if prev_addr == idaapi.BADADDR:
+ return (None, False)
+ else:
+ return (idaapi.cmd.copy(), farref)
+
+
+
+def DecodePreviousInstruction(ea):
+ """
+ Decodes the previous instruction and returns an insn_t like class
+
+ @param ea: address to decode
+ @return: None or a new insn_t instance
+ """
+ inslen = idaapi.decode_prev_insn(ea)
+ if inslen == 0:
+ return None
+
+ return idaapi.cmd.copy()
+
+
+def DecodeInstruction(ea):
+ """
+ Decodes an instruction and returns an insn_t like class
+
+ @param ea: address to decode
+ @return: None or a new insn_t instance
+ """
+ inslen = idaapi.decode_insn(ea)
+ if inslen == 0:
+ return None
+
+ return idaapi.cmd.copy()
+
+
+def GetDataList(ea, count, itemsize=1):
+ """
+ Get data list - INTERNAL USE ONLY
+ """
+ if itemsize == 1:
+ getdata = idaapi.get_byte
+ elif itemsize == 2:
+ getdata = idaapi.get_word
+ elif itemsize == 4:
+ getdata = idaapi.get_long
+ elif itemsize == 8:
+ getdata = idaapi.get_qword
+ else:
+ raise ValueError, "Invalid data size! Must be 1, 2, 4 or 8"
+
+ endea = ea + itemsize * count
+ curea = ea
+ while curea < endea:
+ yield getdata(curea)
+ curea += itemsize
+
+
+def PutDataList(ea, datalist, itemsize=1):
+ """
+ Put data list - INTERNAL USE ONLY
+ """
+ putdata = None
+
+ if itemsize == 1:
+ putdata = idaapi.patch_byte
+ if itemsize == 2:
+ putdata = idaapi.patch_word
+ if itemsize == 4:
+ putdata = idaapi.patch_long
+
+ assert putdata, "Invalid data size! Must be 1, 2 or 4"
+
+ for val in datalist:
+ putdata(ea, val)
+ ea = ea + itemsize
+
+
+def MapDataList(ea, length, func, wordsize=1):
+ """
+ Map through a list of data words in the database
+
+ @param ea: start address
+ @param length: number of words to map
+ @param func: mapping function
+ @param wordsize: size of words to map [default: 1 byte]
+
+ @return: None
+ """
+ PutDataList(ea, map(func, GetDataList(ea, length, wordsize)), wordsize)
+
+
+def GetInputFileMD5():
+ """
+ Return the MD5 hash of the input binary file
+
+ @return: MD5 string or None on error
+ """
+ return idc.GetInputMD5()
+
+
+class Strings(object):
+ """
+ Returns the string list.
+
+ Example:
+ s = Strings()
+
+ for i in s:
+ print "%x: len=%d type=%d -> '%s'" % (i.ea, i.length, i.type, str(i))
+
+ """
+ class StringItem(object):
+ """
+ Class representing each string item.
+ """
+ def __init__(self, si):
+ self.ea = si.ea
+ """String ea"""
+ self.type = si.type
+ """string type (ASCSTR_xxxxx)"""
+ self.length = si.length
+ """string length"""
+
+ def __str__(self):
+ return idc.GetString(self.ea, self.length, self.type)
+
+ STR_C = 0x0001
+ """C-style ASCII string"""
+ STR_PASCAL = 0x0002
+ """Pascal-style ASCII string (length byte)"""
+ STR_LEN2 = 0x0004
+ """Pascal-style, length is 2 bytes"""
+ STR_UNICODE = 0x0008
+ """Unicode string"""
+ STR_LEN4 = 0x0010
+ """Pascal-style, length is 4 bytes"""
+ STR_ULEN2 = 0x0020
+ """Pascal-style Unicode, length is 2 bytes"""
+ STR_ULEN4 = 0x0040
+ """Pascal-style Unicode, length is 4 bytes"""
+
+ def clear_cache(self):
+ """Clears the strings list cache"""
+ self.refresh(0, 0) # when ea1=ea2 the kernel will clear the cache
+
+
+ def __init__(self, default_setup = True):
+ """
+ Initializes the Strings enumeration helper class
+
+ @param default_setup: Set to True to use default setup (C strings, min len 5, ...)
+ """
+ self.size = 0
+ if default_setup:
+ self.setup()
+
+ self._si = idaapi.string_info_t()
+
+
+ def refresh(self, ea1=None, ea2=None):
+ """Refreshes the strings list"""
+ if ea1 is None:
+ ea1 = idaapi.cvar.inf.minEA
+ if ea2 is None:
+ ea2 = idaapi.cvar.inf.maxEA
+
+ idaapi.refresh_strlist(ea1, ea2)
+ self.size = idaapi.get_strlist_qty()
+
+
+ def setup(self,
+ strtypes = STR_C,
+ minlen = 5,
+ only_7bit = True,
+ ignore_instructions = False,
+ ea1 = None,
+ ea2 = None,
+ display_only_existing_strings = False):
+
+ if ea1 is None:
+ ea1 = idaapi.cvar.inf.minEA
+
+ if ea2 is None:
+ ea2 = idaapi.cvar.inf.maxEA
+
+ t = idaapi.strwinsetup_t()
+ t.strtypes = strtypes
+ t.minlen = minlen
+ t.only_7bit = only_7bit
+ t.ea1 = ea1
+ t.ea2 = ea2
+ t.display_only_existing_strings = display_only_existing_strings
+ idaapi.set_strlist_options(t)
+
+ # Automatically refreshes
+ self.refresh()
+
+
+ def _get_item(self, index):
+ if not idaapi.get_strlist_item(index, self._si):
+ return None
+ else:
+ return Strings.StringItem(self._si)
+
+
+ def __iter__(self):
+ return (self._get_item(index) for index in xrange(0, self.size))
+
+
+ def __getitem__(self, index):
+ """Returns a string item or None"""
+ if index >= self.size:
+ raise KeyError
+ else:
+ return self._get_item(index)
+
+# -----------------------------------------------------------------------
+def GetIdbDir():
+ """
+ Get IDB directory
+
+ This function returns directory path of the current IDB database
+ """
+ return os.path.dirname(idaapi.cvar.database_idb) + os.sep
+
+# -----------------------------------------------------------------------
+def GetRegisterList():
+ """Returns the register list"""
+ return idaapi.ph_get_regnames()
+
+# -----------------------------------------------------------------------
+def GetInstructionList():
+ """Returns the instruction list of the current processor module"""
+ return [i[0] for i in idaapi.ph_get_instruc() if i[0]]
+
+# -----------------------------------------------------------------------
+def _Assemble(ea, line):
+ """
+ Please refer to Assemble() - INTERNAL USE ONLY
+ """
+ if type(line) == types.StringType:
+ lines = [line]
+ else:
+ lines = line
+ ret = []
+ for line in lines:
+ seg = idaapi.getseg(ea)
+ if not seg:
+ return (False, "No segment at ea")
+ ip = ea - (idaapi.ask_selector(seg.sel) << 4)
+ buf = idaapi.AssembleLine(ea, seg.sel, ip, seg.bitness, line)
+ if not buf:
+ return (False, "Assembler failed: " + line)
+ ea += len(buf)
+ ret.append(buf)
+
+ if len(ret) == 1:
+ ret = ret[0]
+ return (True, ret)
+
+
+def Assemble(ea, line):
+ """
+ Assembles one or more lines (does not display an message dialogs)
+ If line is a list then this function will attempt to assemble all the lines
+ This function will turn on batch mode temporarily so that no messages are displayed on the screen
+
+ @param ea: start address
+ @return: (False, "Error message") or (True, asm_buf) or (True, [asm_buf1, asm_buf2, asm_buf3])
+ """
+ old_batch = idc.Batch(1)
+ ret = _Assemble(ea, line)
+ idc.Batch(old_batch)
+ return ret
+
+def _copy_obj(src, dest, skip_list = None):
+ """
+ Copy non private/non callable attributes from a class instance to another
+ @param src: Source class to copy from
+ @param dest: If it is a string then it designates the new class type that will be created and copied to.
+ Otherwise dest should be an instance of another class
+ @return: A new instance or "dest"
+ """
+ if type(dest) == types.StringType:
+ # instantiate a new destination class of the specified type name?
+ dest = new.classobj(dest, (), {})
+ for x in dir(src):
+ # skip special and private fields
+ if x.startswith("__") and x.endswith("__"):
+ continue
+ # skip items in the skip list
+ if skip_list and x in skip_list:
+ continue
+ t = getattr(src, x)
+ # skip callable
+ if callable(t):
+ continue
+ setattr(dest, x, t)
+ return dest
+
+# -----------------------------------------------------------------------
+class _reg_dtyp_t(object):
+ """
+ INTERNAL
+ This class describes a register's number and dtyp.
+ The equal operator is overloaded so that two instances can be tested for equality
+ """
+ def __init__(self, reg, dtyp):
+ self.reg = reg
+ self.dtyp = dtyp
+
+ def __eq__(self, other):
+ return (self.reg == other.reg) and (self.dtyp == other.dtyp)
+
+# -----------------------------------------------------------------------
+class _procregs(object):
+ """Utility class allowing the users to identify registers in a decoded instruction"""
+ def __getattr__(self, attr):
+ ri = idaapi.reg_info_t()
+ if not idaapi.parse_reg_name(attr, ri):
+ raise AttributeError()
+ r = _reg_dtyp_t(ri.reg, ord(idaapi.get_dtyp_by_size(ri.size)))
+ self.__dict__[attr] = r
+ return r
+
+ def __setattr__(self, attr, value):
+ raise AttributeError(attr)
+
+# -----------------------------------------------------------------------
+class _cpu(object):
+ "Simple wrapper around GetRegValue/SetRegValue"
+ def __getattr__(self, name):
+ #print "cpu.get(%s)"%name
+ return idc.GetRegValue(name)
+
+ def __setattr__(self, name, value):
+ #print "cpu.set(%s)"%name
+ return idc.SetRegValue(value, name)
+
+# -----------------------------------------------------------------------
+class peutils_t(object):
+ """
+ PE utility class. Retrieves PE information from the database.
+
+ Constants from pe.h
+ """
+ PE_NODE = "$ PE header" # netnode name for PE header
+ PE_ALT_DBG_FPOS = idaapi.BADADDR & -1 # altval() -> translated fpos of debuginfo
+ PE_ALT_IMAGEBASE = idaapi.BADADDR & -2 # altval() -> loading address (usually pe.imagebase)
+ PE_ALT_PEHDR_OFF = idaapi.BADADDR & -3 # altval() -> offset of PE header
+ PE_ALT_NEFLAGS = idaapi.BADADDR & -4 # altval() -> neflags
+ PE_ALT_TDS_LOADED = idaapi.BADADDR & -5 # altval() -> tds already loaded(1) or invalid(-1)
+ PE_ALT_PSXDLL = idaapi.BADADDR & -6 # altval() -> if POSIX(x86) imports from PSXDLL netnode
+
+ def __init__(self):
+ self.__penode = idaapi.netnode()
+ self.__penode.create(peutils_t.PE_NODE)
+
+ imagebase = property(
+ lambda self: self.__penode.altval(peutils_t.PE_ALT_IMAGEBASE)
+ )
+
+ header = property(
+ lambda self: self.__penode.altval(peutils_t.PE_ALT_PEHDR_OFF)
+ )
+
+ def __str__(self):
+ return "peutils_t(imagebase=%s, header=%s)" % (hex(self.imagebase), hex(self.header))
+
+ def header(self):
+ """
+ Returns the complete PE header as an instance of peheader_t (defined in the SDK).
+ """
+ return self.__penode.valobj()
+
+# -----------------------------------------------------------------------
+cpu = _cpu()
+"""This is a special class instance used to access the registers as if they were attributes of this object.
+For example to access the EAX register:
+ print "%x" % cpu.Eax
+"""
+
+procregs = _procregs()
+"""This object is used to access the processor registers. It is useful when decoding instructions and you want to see which instruction is which.
+For example:
+ x = idautils.DecodeInstruction(here())
+ if x[0] == procregs.Esp:
+ print "This operand is the register ESP
"""
\ No newline at end of file
diff --git a/python/idc.py b/python/idc.py
index d00d86f..880efab 100644
--- a/python/idc.py
+++ b/python/idc.py
@@ -702,7 +702,7 @@ def MakeStr(ea, endea):
@note: The type of an existing string is returned by GetStringType()
"""
- return idaapi.make_ascii_string(ea, endea - ea, GetLongPrm(INF_STRTYPE))
+ return idaapi.make_ascii_string(ea, 0 if endea == BADADDR else endea - ea, GetLongPrm(INF_STRTYPE))
def MakeData(ea, flags, size, tid):
@@ -1710,7 +1710,7 @@ def Byte(ea):
might have more 1's.
To check if a byte has a value, use functions hasValue(GetFlags(ea))
"""
- return idaapi.get_byte(ea)
+ return idaapi.get_full_byte(ea)
def __DbgValue(ea, len):
@@ -1781,7 +1781,7 @@ def Word(ea):
If the current byte size is different from 8 bits, then the returned value
might have more 1's.
"""
- return idaapi.get_word(ea)
+ return idaapi.get_full_word(ea)
def Dword(ea):
@@ -1792,7 +1792,7 @@ def Dword(ea):
@return: the value of the double word. If failed returns -1
"""
- return idaapi.get_long(ea)
+ return idaapi.get_full_long(ea)
def Qword(ea):
@@ -1802,10 +1802,8 @@ def Qword(ea):
@param ea: linear address
@return: the value of the quadro word. If failed, returns -1
-
- @note: this function is available only in the 64-bit version of IDA Pro
"""
- raise NotImplementedError, "will be implemented in the 64-bit version"
+ return idaapi.get_qword(ea)
def GetFloat(ea):
@@ -1839,7 +1837,7 @@ def LocByName(name):
@param name: name of program byte
@return: address of the name
- badaddr - no such name
+ BADADDR - No such name
"""
return idaapi.get_name_ea(BADADDR, name)
@@ -1964,7 +1962,7 @@ def PrevAddr(ea):
return idaapi.prevaddr(ea)
-def NextHead(ea, maxea):
+def NextHead(ea, maxea=BADADDR):
"""
Get next defined item (instruction or data) in the program
@@ -1977,7 +1975,7 @@ def NextHead(ea, maxea):
return idaapi.next_head(ea, maxea)
-def PrevHead(ea, minea):
+def PrevHead(ea, minea=0):
"""
Get previous defined item (instruction or data) in the program
@@ -2938,6 +2936,17 @@ def AskLong(defval, prompt):
return idaapi.asklong(defval, prompt)
+def ProcessUiAction(name, flags=0):
+ """
+ Invokes an IDA Pro UI action by name
+
+ @param name: Command name
+ @param flags: Reserved. Must be zero
+ @return: Boolean
+ """
+ return idaapi.process_ui_action(name, flags)
+
+
def AskSeg(defval, prompt):
"""
Ask the user to enter a segment value
@@ -3374,7 +3383,7 @@ def SegByName(segname):
if not seg:
return BADADDR
- return seg.startEA
+ return seg.sel
def SetSegDefReg(ea, reg, value):
@@ -6258,14 +6267,14 @@ def ParseType(inputtype, flags):
"""
return idaapi.idc_parse_decl(idaapi.cvar.idati, inputtype, flags)
-def ParseTypes(inputtype, flags):
+def ParseTypes(inputtype, flags = 0):
"""
Parse type declarations
@param inputtype: file name or C declarations (depending on the flags)
@param flags: combination of PT_... constants or 0
- @return: number of errors
+ @return: number of parsing errors (0 no errors)
"""
return idaapi.idc_parse_types(inputtype, flags)
@@ -6790,7 +6799,6 @@ def GetProcessState():
"""
return idaapi.get_process_state()
-DSTATE_SUSP_FOR_EVENT = -2 # process is currently suspended to react to a debug event (not used)
DSTATE_SUSP = -1 # process is suspended
DSTATE_NOTASK = 0 # no process is currently debugged
DSTATE_RUN = 1 # process is running
@@ -7109,7 +7117,7 @@ def GetRegValue(name):
"""
rv = idaapi.regval_t()
res = idaapi.get_reg_val(name, rv)
- assert res, "get_reg_val() failed, bogus name perhaps?"
+ assert res, "get_reg_val() failed, bogus register name ('%s') perhaps?" % name
return rv.ival
@@ -7207,13 +7215,27 @@ BPT_SOFT = 4 # Software breakpoint
BPTATTR_COUNT = 4
BPTATTR_FLAGS = 5
-BPT_BRK = 0x01 # the debugger stops on this breakpoint
-BPT_TRACE = 0x02 # the debugger adds trace information when this breakpoint is reached
-BPT_UPDMEM = 0x04 # update memory contents before evaluating bpt condition
-BPT_UPDSEG = 0x08 # update memory config before evaluating bpt condition
+BPT_BRK = 0x01 # the debugger stops on this breakpoint
+BPT_TRACE = 0x02 # the debugger adds trace information when this breakpoint is reached
+BPT_UPDMEM = 0x04 # refresh the memory layout and contents before evaluating bpt condition
+BPT_ENABLED = 0x08 # enabled?
+BPT_LOWCND = 0x10 # condition is calculated at low level (on the server side)
BPTATTR_COND = 6 # Breakpoint condition. NOTE: the return value is a string in this case
+# Breakpoint location type:
+BPLT_ABS = 0 # Absolute address. Attributes:
+ # - locinfo: absolute address
+
+BPLT_REL = 1 # Module relative address. Attributes:
+ # - locpath: the module path
+ # - locinfo: offset from the module base address
+
+BPLT_SYM = 2 # Symbolic name. The name will be resolved on DLL load/unload
+ # events and on naming an address. Attributes:
+ # - locpath: symbol name
+ # - locinfo: offset from the symbol base address
+
def SetBptAttr(address, bptattr, value):
"""
@@ -7530,19 +7552,47 @@ def WriteTxt(filepath, ea1, ea2):
def WriteExe(filepath):
return GenerateFile(OFILE_EXE, filepath, 0, BADADDR, 0)
-def AddConst(enum_id,name,value): return AddConstEx(enum_id,name,value, idaapi.BADADDR)
-def AddStruc(index,name): return AddStrucEx(index,name,0)
-def AddUnion(index,name): return AddStrucEx(index,name,1)
-def OpStroff(ea,n,strid): return OpStroffEx(ea,n,strid,0)
-def OpEnum(ea,n,enumid): return OpEnumEx(ea,n,enumid,0)
-def DelConst(constid, v, mask): return DelConstEx(constid, v, 0, mask)
-def GetConst(constid, v, mask): return GetConstEx(constid, v, 0, mask)
-def AnalyseArea(sEA, eEA): return AnalyzeArea(sEA,eEA)
-def MakeStruct(ea,name): return MakeStructEx(ea, -1, name)
-def Name(ea): return NameEx(BADADDR, ea)
-def GetTrueName(ea): return GetTrueNameEx(BADADDR, ea)
-def MakeName(ea, name): return MakeNameEx(ea,name,SN_CHECK)
+UTP_STRUCT = idaapi.UTP_STRUCT
+UTP_ENUM = idaapi.UTP_ENUM
+
+
+def BeginTypeUpdating(utp):
+ """
+ Begin type updating. Use this function if you
+ plan to call AddEnumConst or similar type modification functions
+ many times or from inside a loop
+
+ @param utp: one of UTP_xxxx consts
+ @return: None
+ """
+ return idaapi.begin_type_updating(utp)
+
+
+def EndTypeUpdating(utp):
+ """
+ End type updating. Refreshes the type system
+ at the end of type modification operations
+
+ @param utp: one of idaapi.UTP_xxxx consts
+ @return: None
+ """
+ return idaapi.end_type_updating(utp)
+
+
+def AddConst(enum_id, name,value): return AddConstEx(enum_id, name, value, idaapi.BADADDR)
+def AddStruc(index, name): return AddStrucEx(index,name, 0)
+def AddUnion(index, name): return AddStrucEx(index,name, 1)
+def OpStroff(ea, n, strid): return OpStroffEx(ea,n,strid, 0)
+def OpEnum(ea, n, enumid): return OpEnumEx(ea,n,enumid, 0)
+def DelConst(constid, v, mask): return DelConstEx(constid, v, 0, mask)
+def GetConst(constid, v, mask): return GetConstEx(constid, v, 0, mask)
+def AnalyseArea(sEA, eEA): return AnalyzeArea(sEA,eEA)
+
+def MakeStruct(ea, name): return MakeStructEx(ea, -1, name)
+def Name(ea): return NameEx(BADADDR, ea)
+def GetTrueName(ea): return GetTrueNameEx(BADADDR, ea)
+def MakeName(ea, name): return MakeNameEx(ea,name,SN_CHECK)
#def GetFrame(ea): return GetFunctionAttr(ea, FUNCATTR_FRAME)
#def GetFrameLvarSize(ea): return GetFunctionAttr(ea, FUNCATTR_FRSIZE)
diff --git a/python/init.py b/python/init.py
index 650d5b8..905d4e5 100644
--- a/python/init.py
+++ b/python/init.py
@@ -31,7 +31,7 @@ class IDAPythonStdOut:
# Swap out the unprintable characters
text = text.decode('ascii', 'replace').encode('ascii', 'replace')
# Print to IDA message window
- _idaapi.msg(text.replace("%", "%%"))
+ _idaapi.msg(text)
def flush(self):
pass
@@ -61,9 +61,9 @@ def print_banner():
]
sepline = '-' * (max([len(s) for s in banner])+1)
- print sepline
- print "\n".join(banner)
- print sepline
+ print(sepline)
+ print("\n".join(banner))
+ print(sepline)
# -----------------------------------------------------------------------
@@ -76,9 +76,20 @@ sys.argv = [""]
# Have to make sure Python finds our modules
sys.path.append(_idaapi.idadir("python"))
+# Remove current directory from the top of the patch search
+if '' in sys.path: # On non Windows, the empty path is added
+ sys.path.remove('')
+
+if os.getcwd() in sys.path:
+ sys.path.remove(os.getcwd())
+
+# ...and add it to the end if needed
+if not IDAPYTHON_REMOVE_CWD_SYS_PATH:
+ sys.path.append(os.getcwd())
+
# Import all the required modules
-from idaapi import Choose, get_user_idadir, cvar, Choose2, Appcall
-from idc import *
+from idaapi import Choose, get_user_idadir, cvar, Choose2, Appcall, Form
+from idc import *
from idautils import *
import idaapi
diff --git a/pywraps.hpp b/pywraps.hpp
index 35c91d5..14c4726 100644
--- a/pywraps.hpp
+++ b/pywraps.hpp
@@ -5,6 +5,7 @@
// Types
#ifndef PYUL_DEFINED
#define PYUL_DEFINED
+ typedef unsigned PY_LONG_LONG PY_ULONG_LONG;
#ifdef __EA64__
typedef unsigned PY_LONG_LONG pyul_t;
typedef PY_LONG_LONG pyl_t;
@@ -56,6 +57,31 @@ typedef qvector ppyobject_vec_t;
#define CIP_OK 1 // Success
#define CIP_OK_NODECREF 2 // Success but do not decrement its reference
+//---------------------------------------------------------------------------
+class CGilStateAuto
+{
+private:
+ PyGILState_STATE state;
+public:
+ CGilStateAuto()
+ {
+ state = PyGILState_Ensure();
+ }
+
+ ~CGilStateAuto()
+ {
+ PyGILState_Release(state);
+ }
+};
+// Declare a variable to acquire/release the GIL
+#define PYW_GIL_AUTO_ENSURE CGilStateAuto GIL_STATE_AUTO
+
+// Macros to acquire/release GIL in a given scope
+#define PYW_GIL_ENSURE_N(name) PyGILState_STATE gil_state##name = PyGILState_Ensure()
+#define PYW_GIL_RELEASE_N(name) PyGILState_Release(gil_state##name)
+
+#define PYW_GIL_ENSURE PYW_GIL_ENSURE_N(_)
+#define PYW_GIL_RELEASE PYW_GIL_RELEASE_N(_)
//------------------------------------------------------------------------
// All the exported functions from PyWraps are forward declared here
insn_t *insn_t_get_clink(PyObject *self);
@@ -86,7 +112,8 @@ bool PyW_IsSequenceType(PyObject *obj);
bool PyW_GetError(qstring *out = NULL);
// If an error occured (it calls PyGetError) it displays it and return TRUE
-bool PyW_ShowErr(const char *cb_name);
+// This function is used when calling callbacks
+bool PyW_ShowCbErr(const char *cb_name);
// Utility function to create a class instance whose constructor takes zero arguments
PyObject *create_idaapi_class_instance0(const char *clsname);
@@ -124,6 +151,12 @@ Py_ssize_t pyvar_walk_list(
int (idaapi *cb)(PyObject *py_item, Py_ssize_t index, void *ud) = NULL,
void *ud = NULL);
+// Converts an intvec_t to a Python list object
+PyObject *PyW_IntVecToPyList(const intvec_t &intvec);
+
+// Converts an Python list to an intvec
+void PyW_PyListToIntVec(PyObject *py_list, intvec_t &intvec);
+
// Returns a reference to a class
PyObject *get_idaapi_attr(const char *attr);
@@ -132,4 +165,6 @@ bool pywraps_nw_term();
bool pywraps_nw_notify(int slot, ...);
bool pywraps_nw_init();
+const char *pywraps_check_autoscripts();
+
#endif
\ No newline at end of file
diff --git a/swig/bytes.i b/swig/bytes.i
index a52e76f..75efcc7 100644
--- a/swig/bytes.i
+++ b/swig/bytes.i
@@ -101,7 +101,9 @@
static bool idaapi py_testf_cb(flags_t flags, void *ud)
{
PyObject *py_flags = PyLong_FromUnsignedLong(flags);
+ PYW_GIL_ENSURE;
PyObject *result = PyObject_CallFunctionObjArgs((PyObject *) ud, py_flags, NULL);
+ PYW_GIL_RELEASE;
bool ret = result != NULL && PyObject_IsTrue(result);
Py_XDECREF(result);
Py_XDECREF(py_flags);
@@ -114,7 +116,8 @@ static ea_t py_npthat(ea_t ea, ea_t bound, PyObject *py_callable, bool next)
{
if ( !PyCallable_Check(py_callable) )
return BADADDR;
- return (next ? nextthat : prevthat)(ea, bound, py_testf_cb, py_callable);
+ else
+ return (next ? nextthat : prevthat)(ea, bound, py_testf_cb, py_callable);
}
@@ -134,8 +137,17 @@ class py_custom_data_type_t
size_t nbytes) // size of the future item
{
py_custom_data_type_t *_this = (py_custom_data_type_t *)ud;
- PyObject *py_result = PyObject_CallMethod(_this->py_self, (char *)S_MAY_CREATE_AT, PY_FMT64 PY_FMT64, pyul_t(ea), pyul_t(nbytes));
- PyW_ShowErr(S_MAY_CREATE_AT);
+
+ PYW_GIL_ENSURE;
+ PyObject *py_result = PyObject_CallMethod(
+ _this->py_self,
+ (char *)S_MAY_CREATE_AT,
+ PY_FMT64 PY_FMT64,
+ pyul_t(ea),
+ pyul_t(nbytes));
+ PYW_GIL_RELEASE;
+
+ PyW_ShowCbErr(S_MAY_CREATE_AT);
bool ok = py_result != NULL && PyObject_IsTrue(py_result);
Py_XDECREF(py_result);
return ok;
@@ -152,9 +164,18 @@ class py_custom_data_type_t
// Returns: 0-no such item can be created/displayed
// this callback is required only for varsize datatypes
py_custom_data_type_t *_this = (py_custom_data_type_t *)ud;
- PyObject *py_result = PyObject_CallMethod(_this->py_self, (char *)S_CALC_ITEM_SIZE, PY_FMT64 PY_FMT64, pyul_t(ea), pyul_t(maxsize));
- if ( PyW_ShowErr(S_CALC_ITEM_SIZE) || py_result == NULL )
+ PYW_GIL_ENSURE;
+ PyObject *py_result = PyObject_CallMethod(
+ _this->py_self,
+ (char *)S_CALC_ITEM_SIZE,
+ PY_FMT64 PY_FMT64,
+ pyul_t(ea),
+ pyul_t(maxsize));
+ PYW_GIL_RELEASE;
+
+ if ( PyW_ShowCbErr(S_CALC_ITEM_SIZE) || py_result == NULL )
return 0;
+
uint64 num = 0;
PyW_GetNumber(py_result, &num);
Py_XDECREF(py_result);
@@ -162,11 +183,11 @@ class py_custom_data_type_t
}
public:
- const char *get_name() const
- {
- return dt_name.c_str();
+ const char *get_name() const
+ {
+ return dt_name.c_str();
}
-
+
py_custom_data_type_t()
{
dtid = -1;
@@ -189,6 +210,7 @@ public:
// name
if ( !PyW_GetStringAttr(py_obj, S_NAME, &dt_name) )
break;
+
dt.name = dt_name.c_str();
// menu_name (optional)
@@ -293,11 +315,14 @@ private:
int dtid) // custom data type id
{
// Build a string from the buffer
- PyObject *py_value = PyString_FromStringAndSize((const char *)value, Py_ssize_t(size));
+ PyObject *py_value = PyString_FromStringAndSize(
+ (const char *)value,
+ Py_ssize_t(size));
if ( py_value == NULL )
return false;
py_custom_data_format_t *_this = (py_custom_data_format_t *) ud;
+ PYW_GIL_ENSURE;
PyObject *py_result = PyObject_CallMethod(
_this->py_self,
(char *)S_PRINTF,
@@ -306,11 +331,12 @@ private:
pyul_t(current_ea),
operand_num,
dtid);
+ PYW_GIL_RELEASE;
// Done with the string
Py_DECREF(py_value);
// Error while calling the function?
- if ( PyW_ShowErr(S_PRINTF) || py_result == NULL )
+ if ( PyW_ShowCbErr(S_PRINTF) || py_result == NULL )
return false;
bool ok = false;
@@ -338,6 +364,7 @@ private:
qstring *errstr) // buffer for error message
{
py_custom_data_format_t *_this = (py_custom_data_format_t *) ud;
+ PYW_GIL_ENSURE;
PyObject *py_result = PyObject_CallMethod(
_this->py_self,
(char *)S_SCAN,
@@ -345,9 +372,10 @@ private:
input,
pyul_t(current_ea),
operand_num);
+ PYW_GIL_RELEASE;
// Error while calling the function?
- if ( PyW_ShowErr(S_SCAN) || py_result == NULL)
+ if ( PyW_ShowCbErr(S_SCAN) || py_result == NULL)
return false;
bool ok = false;
@@ -356,6 +384,7 @@ private:
// We expect a tuple(bool, string|None)
if ( !PyTuple_Check(py_result) || PyTuple_Size(py_result) != 2 )
break;
+
// Borrow references
PyObject *py_bool = PyTuple_GetItem(py_result, 0);
PyObject *py_val = PyTuple_GetItem(py_result, 1);
@@ -404,8 +433,17 @@ private:
// this callback may be missing.
{
py_custom_data_format_t *_this = (py_custom_data_format_t *) ud;
- PyObject *py_result = PyObject_CallMethod(_this->py_self, (char *)S_ANALYZE, PY_FMT64 "i", pyul_t(current_ea),operand_num);
- PyW_ShowErr(S_ANALYZE);
+
+ PYW_GIL_ENSURE;
+ PyObject *py_result = PyObject_CallMethod(
+ _this->py_self,
+ (char *)S_ANALYZE,
+ PY_FMT64 "i",
+ pyul_t(current_ea),
+ operand_num);
+ PYW_GIL_RELEASE;
+
+ PyW_ShowCbErr(S_ANALYZE);
Py_XDECREF(py_result);
}
public:
@@ -415,7 +453,10 @@ public:
py_self = NULL;
}
- const char *get_name() const { return df_name.c_str(); }
+ const char *get_name() const
+ {
+ return df_name.c_str();
+ }
int register_df(int dtid, PyObject *py_obj)
{
@@ -491,9 +532,11 @@ public:
Py_INCREF(py_obj);
py_self = py_obj;
+ // Update the format ID
py_attr = PyInt_FromLong(dfid);
PyObject_SetAttrString(py_obj, S_ID, py_attr);
Py_DECREF(py_attr);
+
py_attr = NULL;
} while ( false );
@@ -657,7 +700,7 @@ def register_custom_data_type(dt):
"""
Registers a custom data type.
@param dt: an instance of the data_type_t class
- @return:
+ @return:
< 0 if failed to register
> 0 data type id
"""
@@ -726,7 +769,7 @@ def register_custom_data_format(dtid, df):
Registers a custom data format with a given data type.
@param dtid: data type id
@param df: an instance of data_format_t
- @return:
+ @return:
< 0 if failed to register
> 0 data format id
"""
diff --git a/swig/dbg.i b/swig/dbg.i
index 219f486..36c496a 100644
--- a/swig/dbg.i
+++ b/swig/dbg.i
@@ -10,8 +10,14 @@ typedef struct
%ignore source_file_t;
%ignore source_item_t;
%ignore srcinfo_provider_t;
+%ignore bpt_location_t::print;
+%ignore bpt_t::set_cond;
+%ignore bpt_t::eval_cond;
+%ignore bpt_t::write;
+%ignore bpt_t::erase;
%rename (get_manual_regions) py_get_manual_regions;
%ignore set_manual_regions;
+%ignore inform_idc_about_debthread;
%include "dbg.hpp"
%ignore DBG_Callback;
%feature("director") DBG_Hooks;
@@ -75,8 +81,12 @@ static PyObject *refresh_debugger_memory()
{
invalidate_dbgmem_config();
invalidate_dbgmem_contents(BADADDR, 0);
+
+ // Ask the debugger to populate debug names
if ( dbg != NULL && dbg->stopped_at_debug_event != NULL )
dbg->stopped_at_debug_event(true);
+
+ // Invalidate the cache
isEnabled(0);
Py_RETURN_NONE;
diff --git a/swig/diskio.i b/swig/diskio.i
index 6d280ba..75c2f2b 100644
--- a/swig/diskio.i
+++ b/swig/diskio.i
@@ -49,7 +49,12 @@
int idaapi py_enumerate_files_cb(const char *file, void *ud)
{
PyObject *py_file = PyString_FromString(file);
- PyObject *py_ret = PyObject_CallFunctionObjArgs((PyObject *)ud, py_file, NULL);
+ PYW_GIL_ENSURE;
+ PyObject *py_ret = PyObject_CallFunctionObjArgs(
+ (PyObject *)ud,
+ py_file,
+ NULL);
+ PYW_GIL_RELEASE;
int r = (py_ret == NULL || !PyNumber_Check(py_ret)) ? 1 /* stop enumeration on failure */ : PyInt_AsLong(py_ret);
Py_XDECREF(py_file);
Py_XDECREF(py_ret);
diff --git a/swig/expr.i b/swig/expr.i
index 18ded5d..4f79e3a 100644
--- a/swig/expr.i
+++ b/swig/expr.i
@@ -35,15 +35,17 @@
%ignore expr_printf;
%ignore expr_sprintf;
%ignore expr_printfer;
-%ignore idaapi init_idc;
-%ignore idaapi term_idc;
-%ignore del_idc_userfuncs;
-%ignore del_idc_userdefs;
+%ignore init_idc;
+%ignore term_idc;
+%ignore create_default_idc_classes;
+%ignore insn_to_idc;
%ignore find_builtin_idc_func;
+%ignore idc_mutex;
%ignore idc_lx;
%ignore idc_vars;
%ignore idc_resolve_label;
%ignore idc_resolver_ea;
+%ignore setup_lowcnd_regfuncs;
%cstring_output_maxstr_none(char *errbuf, size_t errbufsize);
@@ -83,12 +85,13 @@ bool calc_idc_expr_wrap(ea_t where,const char *line, idc_value_t *rv, char *errb
%}
%ignore CompileLine(const char *line, char *errbuf, size_t errbufsize, uval_t (idaapi*_getname)(const char *name)=NULL);
+%ignore CompileLineEx;
%rename (CompileLine) CompileLine_wrap;
%inline %{
bool CompileLine_wrap(const char *line, char *errbuf, size_t errbufsize)
{
- return !CompileLine(line, errbuf, errbufsize);
+ return !CompileLineEx(line, errbuf, errbufsize);
}
%}
diff --git a/swig/funcs.i b/swig/funcs.i
index b2b5503..4551eb7 100644
--- a/swig/funcs.i
+++ b/swig/funcs.i
@@ -27,6 +27,7 @@
%ignore create_func_eas_array;
%ignore auto_add_func_tails;
%ignore read_tails;
+%rename (get_idasgn_desc) py_get_idasgn_desc;
%include "funcs.hpp"
@@ -34,18 +35,47 @@
%clear(char *optlibs);
%inline %{
+//-----------------------------------------------------------------------
/*
#
def get_fchunk_referer(ea, idx):
pass
#
*/
-ea_t get_fchunk_referer(ea_t ea, size_t idx)
+static ea_t get_fchunk_referer(ea_t ea, size_t idx)
{
func_t *pfn = get_fchunk(ea);
func_parent_iterator_t dummy(pfn); // read referer info
if (idx >= pfn->refqty || pfn->referers == NULL)
- return BADADDR;
- return pfn->referers[idx];
+ return BADADDR;
+ else
+ return pfn->referers[idx];
}
+
+//-----------------------------------------------------------------------
+/*
+#
+def get_idasgn_desc(n):
+ """
+ Get information about a signature in the list.
+ It returns both:
+ signame - the name of the signature
+ optlibs - the names of the optional libraries
+
+ @param n: number of signature in the list (0..get_idasgn_qty()-1)
+ @return: None on failure or tuple(signame, optlibs)
+ """
+#
+*/
+static PyObject *ida_export py_get_idasgn_desc(int n)
+{
+ char signame[MAXSTR];
+ char optlibs[MAXSTR];
+
+ if ( get_idasgn_desc(n, signame, sizeof(signame), optlibs, sizeof(optlibs)) == -1 )
+ Py_RETURN_NONE;
+ else
+ return Py_BuildValue("(ss)", signame, optlibs);
+}
+
%}
diff --git a/swig/gdl.i b/swig/gdl.i
index 239b2c3..0443aac 100644
--- a/swig/gdl.i
+++ b/swig/gdl.i
@@ -91,6 +91,14 @@ class FlowChart(object):
self._q.refresh()
+ def _getitem(self, index):
+ return BasicBlock(index, self._q[index], self)
+
+
+ def __iter__(self):
+ return (self._getitem(index) for index in xrange(0, self.size))
+
+
def __getitem__(self, index):
"""
Returns a basic block
@@ -98,9 +106,9 @@ class FlowChart(object):
@return: BasicBlock
"""
if index >= self.size:
- raise StopIteration
+ raise KeyError
else:
- return BasicBlock(index, self._q[index], self)
+ return self._getitem(index)
#
%}
diff --git a/swig/graph.i b/swig/graph.i
index 3faeefc..9367237 100644
--- a/swig/graph.i
+++ b/swig/graph.i
@@ -2,26 +2,6 @@
%ignore graph_visitor_t;
%ignore abstract_graph_t;
-%{
-#ifdef __GNUC__
-// for some reason GCC insists on putting the vtable into the object file,
-// even though we only use mutable_graph_t by pointer
-// so we define these methods here to make the linker happy
-
-edge_info_t *idaapi mutable_graph_t::get_edge(edge_t e) { INTERR(); };
-mutable_graph_t *idaapi mutable_graph_t::clone(void) const { INTERR(); };
-bool idaapi mutable_graph_t::redo_layout(void) { INTERR(); };
-void idaapi mutable_graph_t::resize(int n) { INTERR(); };
-int idaapi mutable_graph_t::add_node(const rect_t *r) { INTERR(); };
-ssize_t idaapi mutable_graph_t::del_node(int n) { INTERR(); };
-bool idaapi mutable_graph_t::add_edge(int i, int j, const edge_info_t *ei) { INTERR(); };
-bool idaapi mutable_graph_t::del_edge(int i, int j) { INTERR(); };
-bool idaapi mutable_graph_t::replace_edge(int i, int j, int x, int y) { INTERR(); };
-bool idaapi mutable_graph_t::refresh(void) { INTERR(); };
-bool idaapi mutable_graph_t::set_nrect(int n, const rect_t &r) { INTERR(); };
-#endif
-%}
-
%{
//
class py_graph_t
@@ -46,6 +26,7 @@ private:
nodetext_cache_t(const char *t, bgcolor_t c): text(t), bgcolor(c) { }
nodetext_cache_t() { }
};
+
class nodetext_cache_map_t: public std::map
{
public:
@@ -58,7 +39,7 @@ private:
}
nodetext_cache_t *add(const int node_id, const char *text, bgcolor_t bgcolor = DEFCOLOR)
{
- return &(insert(std::make_pair(node_id, nodetext_cache_t(text, bgcolor))).first->second);
+ return &(insert(std::make_pair(node_id, nodetext_cache_t(text, bgcolor))).first->second);
}
};
@@ -75,6 +56,7 @@ private:
(*this)[form] = py;
}
};
+
class cmdid_map_t: public std::map
{
private:
@@ -83,7 +65,8 @@ private:
cmdid_map_t()
{
- uid = 1; // we start by one and keep zero for error id
+ // We start by one and keep zero for error id
+ uid = 1;
}
void add(py_graph_t *pyg)
@@ -92,7 +75,10 @@ private:
++uid;
}
- const Py_ssize_t id() const { return uid; }
+ const Py_ssize_t id() const
+ {
+ return uid;
+ }
void clear(py_graph_t *pyg)
{
@@ -108,6 +94,7 @@ private:
++it;
}
}
+
py_graph_t *get(Py_ssize_t id)
{
iterator it = find(id);
@@ -136,13 +123,16 @@ private:
py_graph_t *_this = cmdid_pyg.get(id);
if ( _this != NULL )
_this->on_command(id);
+
return true;
}
void on_command(Py_ssize_t id)
{
// Check return value to OnRefresh() call
+ PYW_GIL_ENSURE;
PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_COMMAND, "n", id);
+ PYW_GIL_RELEASE;
Py_XDECREF(ret);
}
@@ -155,7 +145,9 @@ private:
return;
// Check return value to OnRefresh() call
+ PYW_GIL_ENSURE;
PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_REFRESH, NULL);
+ PYW_GIL_RELEASE;
if ( ret == NULL || !PyObject_IsTrue(ret) )
{
Py_XDECREF(ret);
@@ -213,11 +205,14 @@ private:
Py_DECREF(id);
if ( v > max_nodes )
break;
+
edge_ids[j] = v;
}
+
// Incomplete?
if ( j != qnumber(edge_ids) )
break;
+
// Add the edge
g->add_edge(edge_ids[0], edge_ids[1], NULL);
}
@@ -240,7 +235,9 @@ private:
}
// Not cached, call Python
+ PYW_GIL_ENSURE;
PyObject *result = PyObject_CallMethod(self, (char *)S_ON_GETTEXT, "i", node);
+ PYW_GIL_RELEASE;
if ( result == NULL )
return false;
@@ -276,6 +273,7 @@ private:
*str = c->text.c_str();
if ( bg_color != NULL )
*bg_color = c->bgcolor;
+
return true;
}
@@ -290,7 +288,9 @@ private:
if ( mousenode == -1 )
return 0;
+ PYW_GIL_ENSURE;
PyObject *result = PyObject_CallMethod(self, (char *)S_ON_HINT, "i", mousenode);
+ PYW_GIL_RELEASE;
bool ok = result != NULL && PyString_Check(result);
if ( !ok )
{
@@ -307,24 +307,33 @@ private:
{
if ( self != NULL )
{
- if ( cb_flags & GR_HAVE_CLOSE )
+ if ( (cb_flags & GR_HAVE_CLOSE) != 0 )
{
+ PYW_GIL_ENSURE;
PyObject *result = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL);
+ PYW_GIL_RELEASE;
+
Py_XDECREF(result);
}
unbind();
}
+
// Remove the TForm from list
if ( form != NULL )
tform_pyg.erase(form);
- // remove all associated commands from the list
+
+ // Remove all associated commands from the list
cmdid_pyg.clear(this);
+
// Delete this instance
delete this;
}
// graph is being clicked
- int on_clicked(graph_viewer_t * /*gv*/, selection_item_t * /*item1*/, graph_item_t *item2)
+ int on_clicked(
+ graph_viewer_t * /*gv*/,
+ selection_item_t * /*item1*/,
+ graph_item_t *item2)
{
// in: graph_viewer_t *gv
// selection_item_t *current_item1
@@ -338,7 +347,14 @@ private:
if ( item2->n == -1 )
return 1;
- PyObject *result = PyObject_CallMethod(self, (char *)S_ON_CLICK, "i", item2->n);
+ PYW_GIL_ENSURE;
+ PyObject *result = PyObject_CallMethod(
+ self,
+ (char *)S_ON_CLICK,
+ "i",
+ item2->n);
+ PYW_GIL_RELEASE;
+
if ( result == NULL || !PyObject_IsTrue(result) )
{
Py_XDECREF(result);
@@ -360,7 +376,15 @@ private:
//selection_item_t *s = va_arg(va, selection_item_t *);
if ( item == NULL || !item->is_node )
return 1;
- PyObject *result = PyObject_CallMethod(self, (char *)S_ON_DBL_CLICK, "i", item->node);
+
+ PYW_GIL_ENSURE;
+ PyObject *result = PyObject_CallMethod(
+ self,
+ (char *)S_ON_DBL_CLICK,
+ "i",
+ item->node);
+ PYW_GIL_RELEASE;
+
if ( result == NULL || !PyObject_IsTrue(result) )
{
Py_XDECREF(result);
@@ -373,14 +397,25 @@ private:
// a graph viewer got focus
void on_gotfocus(graph_viewer_t * /*gv*/)
{
- PyObject *result = PyObject_CallMethod(self, (char *)S_ON_ACTIVATE, NULL);
+ PYW_GIL_ENSURE;
+ PyObject *result = PyObject_CallMethod(
+ self,
+ (char *)S_ON_ACTIVATE,
+ NULL);
+ PYW_GIL_RELEASE;
Py_XDECREF(result);
}
// a graph viewer lost focus
void on_lostfocus(graph_viewer_t *gv)
{
- PyObject *result = PyObject_CallMethod(self, (char *)S_ON_DEACTIVATE, NULL);
+ PYW_GIL_ENSURE;
+ PyObject *result = PyObject_CallMethod(
+ self,
+ (char *)S_ON_DEACTIVATE,
+ NULL);
+ PYW_GIL_RELEASE;
+
Py_XDECREF(result);
}
@@ -392,7 +427,15 @@ private:
// out: 0-ok, 1-forbid to change the current node
if ( curnode < 0 )
return 0;
- PyObject *result = PyObject_CallMethod(self, (char *)S_ON_SELECT, "i", curnode);
+
+ PYW_GIL_ENSURE;
+ PyObject *result = PyObject_CallMethod(
+ self,
+ (char *)S_ON_SELECT,
+ "i",
+ curnode);
+ PYW_GIL_RELEASE;
+
bool allow = (result != NULL && PyObject_IsTrue(result));
Py_XDECREF(result);
return allow ? 0 : 1;
@@ -428,7 +471,10 @@ private:
ret = on_clicked(gv, item, gitem);
}
else
- ret = 1; // ignore click
+ {
+ // Ignore the click
+ ret = 1;
+ }
break;
//
case grcode_dblclicked:
@@ -445,17 +491,20 @@ private:
case grcode_gotfocus:
if ( cb_flags & GR_HAVE_GOTFOCUS )
on_gotfocus(va_arg(va, graph_viewer_t *));
+
ret = 0;
break;
//
case grcode_lostfocus:
if ( cb_flags & GR_HAVE_LOSTFOCUS )
on_lostfocus(va_arg(va, graph_viewer_t *));
+
ret = 0;
break;
//
case grcode_user_refresh:
on_user_refresh(va_arg(va, mutable_graph_t *));
+
ret = 1;
break;
//
@@ -617,7 +666,7 @@ private:
netnode id;
id.create();
gv = create_graph_viewer(form, id, s_callback, this, 0);
- open_tform(form, FORM_MDI|FORM_TAB|FORM_MENU);
+ open_tform(form, FORM_MDI | FORM_TAB | FORM_MENU);
if ( gv != NULL )
viewer_fit_window(gv);
}
@@ -625,6 +674,7 @@ private:
{
show();
}
+
viewer_fit_window(gv);
return 0;
}
@@ -655,6 +705,7 @@ private:
py_graph_t *_this = extract_this(self);
if ( _this == NULL || _this->form == NULL )
return;
+
_this->jump_to_node(0);
}
@@ -663,6 +714,7 @@ private:
py_graph_t *_this = extract_this(self);
if ( _this == NULL || _this->form == NULL )
return 0;
+
return _this->add_command(title, hotkey);
}
@@ -671,6 +723,7 @@ private:
py_graph_t *_this = extract_this(self);
if ( _this == NULL || _this->form == NULL )
return;
+
close_tform(_this->form, 0);
}
@@ -679,6 +732,7 @@ private:
py_graph_t *_this = extract_this(self);
if ( _this == NULL )
return;
+
_this->refresh();
}
@@ -763,7 +817,7 @@ bool pyg_show(PyObject *self);
%pythoncode %{
#
-class GraphViewer:
+class GraphViewer(object):
"""This class wraps the user graphing facility provided by the graph.hpp file"""
def __init__(self, title, close_open = False):
"""
@@ -793,11 +847,17 @@ class GraphViewer:
self._nodes = []
self._edges = []
+
+ def __iter__(self):
+ return (self._nodes[index] for index in xrange(0, len(self._nodes)))
+
+
def __getitem__(self, idx):
"""Returns a reference to the object associated with this node id"""
- if idx > len(self._nodes):
- raise StopIteration
- return self._nodes[idx]
+ if idx >= len(self._nodes):
+ raise KeyError
+ else:
+ return self._nodes[idx]
def Count(self):
"""Returns the node count"""
diff --git a/swig/idaapi.i b/swig/idaapi.i
index 28ad909..1cc193e 100644
--- a/swig/idaapi.i
+++ b/swig/idaapi.i
@@ -77,1175 +77,17 @@
#include "fpro.h"
#include
+
+class Choose:
+ """
+ Choose - class for choose() with callbacks
+ """
+ def __init__(self, list, title, flags=0):
+ self.list = list
+ self.title = title
+
+ self.flags = flags
+ self.x0 = -1
+ self.x1 = -1
+ self.y0 = -1
+ self.y1 = -1
+
+ self.width = -1
+
+ # HACK: Add a circular reference for non-modal choosers. This prevents the GC
+ # from collecting the class object the callbacks need. Unfortunately this means
+ # that the class will never be collected, unless refhack is set to None explicitly.
+ if (flags & Choose2.CH_MODAL) == 0:
+ self.refhack = self
+
+ def sizer(self):
+ """
+ Callback: sizer - returns the length of the list
+ """
+ return len(self.list)
+
+ def getl(self, n):
+ """
+ Callback: getl - get one item from the list
+ """
+ if n == 0:
+ return self.title
+ if n <= self.sizer():
+ return str(self.list[n-1])
+ else:
+ return ""
+
+
+ def ins(self):
+ pass
+
+
+ def update(self, n):
+ pass
+
+
+ def edit(self, n):
+ pass
+
+
+ def enter(self, n):
+ print "enter(%d) called" % n
+
+
+ def destroy(self):
+ pass
+
+
+ def get_icon(self, n):
+ pass
+
+
+ def choose(self):
+ """
+ choose - Display the choose dialogue
+ """
+ old = set_script_timeout(0)
+ n = _idaapi.choose_choose(self, self.flags, self.x0, self.y0, self.x1, self.y1, self.width)
+ set_script_timeout(old)
+ return n
%}
%pythoncode %{
diff --git a/swig/lines.i b/swig/lines.i
index 7828aad..ce901b4 100644
--- a/swig/lines.i
+++ b/swig/lines.i
@@ -78,7 +78,6 @@
//
//------------------------------------------------------------------------
static PyObject *py_get_user_defined_prefix = NULL;
-static qstring py_get_user_defined_prefix_err;
static void idaapi s_py_get_user_defined_prefix(
ea_t ea,
int lnnum,
@@ -92,12 +91,9 @@ static void idaapi s_py_get_user_defined_prefix(
PY_FMT64 "iis" PY_FMT64,
ea, lnnum, indent, line, bufsize);
- if ( PyW_GetError(&py_get_user_defined_prefix_err) || py_ret == NULL )
- {
- msg("py_get_user_defined_prefix() error: %s\n", py_get_user_defined_prefix_err.c_str());
- PyErr_Clear();
- }
- else
+ // Error? Display it
+ // No error? Copy the buffer
+ if ( !PyW_ShowCbErr("py_get_user_defined_prefix") )
{
Py_ssize_t py_len;
char *py_str;
@@ -192,6 +188,7 @@ PyObject *py_tag_remove(const char *instr)
char *buf = new char[sz + 5];
if ( buf == NULL )
Py_RETURN_NONE;
+
ssize_t r = tag_remove(instr, buf, sz);
PyObject *res;
if ( r < 0 )
diff --git a/swig/loader.i b/swig/loader.i
index 6971548..d1476f5 100644
--- a/swig/loader.i
+++ b/swig/loader.i
@@ -12,6 +12,7 @@
// TODO: These could be wrapped if needed
%ignore load_info_t;
%ignore add_plugin_option;
+%ignore get_plugins_path;
%ignore build_loaders_list;
%ignore free_loaders_list;
%ignore get_loader_name_from_dll;
@@ -147,6 +148,7 @@ static int py_mem2base(PyObject *py_mem, ea_t ea, long fpos = -1)
char *buf;
if ( PyString_AsStringAndSize(py_mem, &buf, &len) == -1 )
return 0;
+
return mem2base((void *)buf, ea, ea+len, fpos);
}
@@ -168,6 +170,7 @@ static PyObject *py_load_plugin(const char *name)
plugin_t *r = load_plugin(name);
if ( r == NULL )
Py_RETURN_NONE;
+
return PyCObject_FromVoidPtr(r, NULL);
}
@@ -187,6 +190,7 @@ static bool py_run_plugin(PyObject *plg, int arg)
{
if ( !PyCObject_Check(plg) )
return false;
+
return run_plugin((plugin_t *)PyCObject_AsVoidPtr(plg), arg);
}
diff --git a/swig/nalt.i b/swig/nalt.i
index c8600b6..bb78ec8 100644
--- a/swig/nalt.i
+++ b/swig/nalt.i
@@ -25,7 +25,11 @@ static int idaapi py_import_enum_cb(
uval_t ord,
void *param)
{
- PyObject *py_ea = Py_BuildValue(PY_FMT64, pyul_t(ea));
+ // If no name, try to get the name associated with the 'ea'. It may be coming from IDS
+ char name_buf[MAXSTR];
+ if ( name == NULL )
+ name = get_true_name(BADADDR, ea, name_buf, sizeof(name_buf));
+
PyObject *py_name;
if ( name == NULL )
{
@@ -36,13 +40,25 @@ static int idaapi py_import_enum_cb(
{
py_name = PyString_FromString(name);
}
+
PyObject *py_ord = Py_BuildValue(PY_FMT64, pyul_t(ord));
- PyObject *py_result = PyObject_CallFunctionObjArgs((PyObject *)param, py_ea, py_name, py_ord, NULL);
+ PyObject *py_ea = Py_BuildValue(PY_FMT64, pyul_t(ea));
+ PYW_GIL_ENSURE;
+ PyObject *py_result = PyObject_CallFunctionObjArgs(
+ (PyObject *)param,
+ py_ea,
+ py_name,
+ py_ord,
+ NULL);
+ PYW_GIL_RELEASE;
+
int r = py_result != NULL && PyObject_IsTrue(py_result) ? 1 : 0;
+
Py_DECREF(py_ea);
Py_DECREF(py_name);
Py_DECREF(py_ord);
Py_XDECREF(py_result);
+
return r;
}
@@ -90,6 +106,7 @@ static PyObject *py_get_import_module_name(int mod_index)
char buf[MAXSTR];
if ( !get_import_module_name(mod_index, buf, sizeof(buf)) )
Py_RETURN_NONE;
+
return PyString_FromString(buf);
}
@@ -234,6 +251,7 @@ static int py_enum_import_names(int mod_index, PyObject *py_cb)
{
if ( !PyCallable_Check(py_cb) )
return -1;
+
return enum_import_names(mod_index, py_import_enum_cb, py_cb);
}
@@ -244,6 +262,7 @@ static PyObject *switch_info_ex_t_create()
return PyCObject_FromVoidPtr(inst, NULL);
}
+//---------------------------------------------------------------------------
static bool switch_info_ex_t_destroy(PyObject *py_obj)
{
if ( !PyCObject_Check(py_obj) )
diff --git a/swig/name.i b/swig/name.i
index c216e98..94c47ed 100644
--- a/swig/name.i
+++ b/swig/name.i
@@ -45,7 +45,7 @@ PyObject *py_get_debug_names(ea_t ea1, ea_t ea2)
for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it)
{
PyDict_SetItem(dict,
- PyInt_FromSize_t(it->ea),
+ Py_BuildValue(PY_FMT64, it->ea),
PyString_FromString(it->name.c_str()));
}
return dict;
@@ -63,12 +63,14 @@ class NearestName:
def __init__(self, ea_names):
self.update(ea_names)
+
def update(self, ea_names):
"""Updates the ea/names map"""
self._names = ea_names
self._addrs = ea_names.keys()
self._addrs.sort()
+
def find(self, ea):
"""
Returns a tupple (ea, name, pos) that is the nearest to the passed ea
@@ -85,13 +87,21 @@ class NearestName:
return None
return self[pos]
+
+ def _get_item(self, index):
+ ea = self._addrs[index]
+ return (ea, self._names[ea], index)
+
+
+ def __iter__(self):
+ return (self._get_item(index) for index in xrange(0, len(self._addrs)))
+
+
def __getitem__(self, index):
"""Returns the tupple (ea, name, index)"""
if index > len(self._addrs):
raise StopIteration
- ea = self._addrs[index]
- return (ea, self._names[ea], index)
+ return self._get_item(index)
%}
%include "name.hpp"
-
diff --git a/swig/netnode.i b/swig/netnode.i
index 63725ac..884e366 100644
--- a/swig/netnode.i
+++ b/swig/netnode.i
@@ -88,4 +88,12 @@
// Renaming one version of hashset() otherwise SWIG will not be able to activate the other one
%rename (hashset_idx) netnode::hashset(const char *idx, nodeidx_t value, char tag=htag);
-%include "netnode.hpp"
\ No newline at end of file
+%include "netnode.hpp"
+
+%extend netnode
+{
+ nodeidx_t index()
+ {
+ return self->operator nodeidx_t();
+ }
+}
\ No newline at end of file
diff --git a/swig/pro.i b/swig/pro.i
index 55a3610..85cb2cf 100644
--- a/swig/pro.i
+++ b/swig/pro.i
@@ -23,7 +23,14 @@
%ignore qthread_join;
%ignore qthread_kill;
%ignore qthread_self;
+%ignore qthread_same;
%ignore qthread_t;
+%ignore qhandle_t;
+%ignore qpipe_create;
+%ignore qpipe_read;
+%ignore qpipe_write;
+%ignore qpipe_close;
+%ignore qwait_for_handles;
%ignore qstrlen;
%ignore qstrcmp;
%ignore qstrstr;
@@ -65,8 +72,11 @@
%ignore swap_value;
%ignore qalloc_or_throw;
%ignore qrealloc_or_throw;
-%ignore launch_process_t;
-%ignore init_process;
+%ignore get_buffer_for_sysdir;
+%ignore get_buffer_for_winerr;
+%ignore call_atexits;
+%ignore launch_process_params_t;
+%ignore launch_process;
%ignore term_process;
%ignore get_process_exit_code;
%ignore BELOW_NORMAL_PRIORITY_CLASS;
diff --git a/swig/typeconv.i b/swig/typeconv.i
index 0c2f1b9..13c9cc5 100644
--- a/swig/typeconv.i
+++ b/swig/typeconv.i
@@ -135,7 +135,7 @@
else
{
Py_INCREF(Py_None);
- resultobj = Py_None;
+ resultobj = Py_None;
}
#ifdef __cplusplus
delete [] (char *)$1;
@@ -148,8 +148,20 @@
// Check that the argument is a callable Python object
%typemap(in) PyObject *pyfunc {
if (!PyCallable_Check($input)) {
- PyErr_SetString(PyExc_TypeError, "Expecting a callable object");
+ PyErr_SetString(PyExc_TypeError, "Expected a callable object");
return NULL;
}
$1 = $input;
}
+
+// Convert ea_t
+%typemap(in) ea_t
+{
+ uint64 $1_temp;
+ if ( !PyW_GetNumber($input, &$1_temp) )
+ {
+ PyErr_SetString(PyExc_TypeError, "Expected an ea_t type");
+ return NULL;
+ }
+ $1 = ea_t($1_temp);
+}
diff --git a/swig/typeinf.i b/swig/typeinf.i
index 027b04c..75e57ce 100644
--- a/swig/typeinf.i
+++ b/swig/typeinf.i
@@ -62,6 +62,9 @@
%ignore print_type;
%ignore show_type;
%ignore show_plist;
+%ignore skip_function_arg_names;
+%ignore perform_funcarg_conversion;
+%ignore get_argloc_info;
%ignore extract_pstr;
%ignore extract_name;
@@ -167,6 +170,7 @@ PyObject *idc_parse_decl(til_t *ti, const char *decl, int flags)
bool ok = parse_decl(ti, decl, &name, &type, &fields, flags);
if ( !ok )
Py_RETURN_NONE;
+
return Py_BuildValue("(sss)",
name.c_str(),
(char *)type.c_str(),
@@ -256,7 +260,14 @@ PyObject *py_unpack_object_from_idb(
type_t *type = (type_t *) PyString_AsString(py_type);
p_list *fields = (p_list *) PyString_AsString(py_fields);
idc_value_t idc_obj;
- error_t err = unpack_object_from_idb(&idc_obj, ti, type, fields, ea, NULL, pio_flags);
+ error_t err = unpack_object_from_idb(
+ &idc_obj,
+ ti,
+ type,
+ fields,
+ ea,
+ NULL,
+ pio_flags);
// Unpacking failed?
if ( err != eOk )
@@ -265,9 +276,11 @@ PyObject *py_unpack_object_from_idb(
// Convert
PyObject *py_ret(NULL);
err = idcvar_to_pyvar(idc_obj, &py_ret);
+
// Conversion failed?
if ( err != CIP_OK )
return Py_BuildValue("(ii)", 0, err);
+
PyObject *py_result = Py_BuildValue("(iO)", 1, py_ret);
Py_DECREF(py_ret);
return py_result;
@@ -315,7 +328,13 @@ PyObject *py_unpack_object_from_bv(
memcpy(bytes.begin(), PyString_AsString(py_bytes), bytes.size());
idc_value_t idc_obj;
- error_t err = unpack_object_from_bv(&idc_obj, ti, type, fields, bytes, pio_flags);
+ error_t err = unpack_object_from_bv(
+ &idc_obj,
+ ti,
+ type,
+ fields,
+ bytes,
+ pio_flags);
// Unpacking failed?
if ( err != eOk )
@@ -324,9 +343,11 @@ PyObject *py_unpack_object_from_bv(
// Convert
PyObject *py_ret(NULL);
err = idcvar_to_pyvar(idc_obj, &py_ret);
+
// Conversion failed?
if ( err != CIP_OK )
return Py_BuildValue("(ii)", 0, err);
+
PyObject *py_result = Py_BuildValue("(iO)", 1, py_ret);
Py_DECREF(py_ret);
return py_result;
diff --git a/swig/ua.i b/swig/ua.i
index 36e1fe9..bf7b659 100644
--- a/swig/ua.i
+++ b/swig/ua.i
@@ -64,6 +64,7 @@ PyObject *py_init_output_buffer(size_t size = MAXSTR)
PyObject *py_str = PyString_FromStringAndSize(NULL, size);
if ( py_str == NULL )
Py_RETURN_NONE;
+
init_output_buffer(PyString_AsString(py_str), size);
return py_str;
}
@@ -326,6 +327,7 @@ bool py_out_name_expr(
off = adiff_t(v);
else
off = BADADDR;
+
return op == NULL ? false : out_name_expr(*op, ea, off);
}
@@ -449,6 +451,7 @@ static void insn_t_set_cs(PyObject *self, PyObject *value)
insn_t *link = insn_t_get_clink(self);
if ( link == NULL )
return;
+
uint64 v(0);
PyW_GetNumber(value, &v);
link->cs = ea_t(v);
@@ -886,7 +889,7 @@ class op_t(py_clinked_object_t):
def is_reg(self, r):
"""Checks if the register operand is the given processor register"""
- return self.type == idaapi.o_reg and self == r
+ return self.type == _idaapi.o_reg and self == r
def has_reg(self, r):
"""Checks if the operand accesses the given processor register"""
@@ -1000,15 +1003,24 @@ class insn_t(py_clinked_object_t):
def _create_clink(self):
return _idaapi.insn_t_create()
+
def _del_clink(self, lnk):
return _idaapi.insn_t_destroy(lnk)
+
+ def __iter__(self):
+ return (self.Operands[idx] for idx in xrange(0, UA_MAXOP))
+
+
def __getitem__(self, idx):
"""
Operands can be accessed directly as indexes
@return op_t: Returns an operand of type op_t
"""
- return self.Operands[idx]
+ if idx >= UA_MAXOP:
+ raise KeyError
+ else:
+ return self.Operands[idx]
def is_macro(self):
return self.flags & INSN_MACRO != 0