From 3a5063330cc57aa85035e6f3182ecdb847a4d0d8 Mon Sep 17 00:00:00 2001 From: "elias.bachaalany" Date: Mon, 28 Jun 2010 12:36:40 +0000 Subject: [PATCH] IDAPython 1.4.0 - IDA Pro 5.7 support --- BUILDING.txt | 123 +- CHANGES.txt | 222 +- COPYING.txt | 54 +- README.txt | 172 +- STATUS.txt | 132 +- build.py | 83 +- examples/ex_add_menu_item.py | 18 + examples/ex_custview.py | 9 +- python.cpp | 2435 +++--- python/idautils.py | 1113 ++- python/idc.py | 15081 +++++++++++++++++---------------- python/init.py | 307 +- pywraps.hpp | 122 +- swig/bytes.i | 960 ++- swig/dbg.i | 552 +- swig/diskio.i | 120 +- swig/enum.i | 58 +- swig/expr.i | 12 + swig/fpro.i | 80 +- swig/frame.i | 10 + swig/funcs.i | 96 +- swig/gdl.i | 207 +- swig/graph.i | 1821 ++-- swig/ida.i | 46 +- swig/idaapi.i | 3230 ++++--- swig/idd.i | 410 +- swig/idp.i | 951 ++- swig/kernwin.i | 4732 ++++++----- swig/lines.i | 409 +- swig/loader.i | 2 + swig/nalt.i | 612 +- swig/netnode.i | 2 + swig/pro.i | 62 +- swig/queue.i | 4 +- swig/srarea.i | 1 + swig/strlist.i | 22 +- swig/typeinf.i | 140 +- swig/ua.i | 1369 ++- swig/xref.i | 2 + tools/swigdocs.py | 85 + 40 files changed, 20591 insertions(+), 15275 deletions(-) create mode 100644 examples/ex_add_menu_item.py create mode 100644 tools/swigdocs.py diff --git a/BUILDING.txt b/BUILDING.txt index f3aca99..256fb1e 100644 --- a/BUILDING.txt +++ b/BUILDING.txt @@ -1,63 +1,60 @@ ----------------------------------------------------------- -IDAPython - Python plugin for Interactive Disassembler Pro ----------------------------------------------------------- -Building From Source --------------------- - -REQUIREMENTS ------------- - -[Tested versions are in brackets] - - - IDA and IDA SDK [5.6] - http://www.hex-rays.com/idapro/ - - - Python [2.5.1, 2.6.1] - http://www.python.org/ - - - Simplified Wrapper Interface Generator (SWIG) [1.3.36] - http://www.swig.org/ - - - Unix utilities (GNU patch on Windows): - http://www.research.att.com/sw/tools/uwin/ or - http://unxutils.sourceforge.net/ or - http://www.cygwin.com/ - - - GCC on Linux and Mac OS X [4.0.1, 4.1.3] - Comes with your distribution - - - Microsoft Visual C on Windows [Microsoft Visual C++ 2008 Express Edition] - http://msdn.microsoft.com/vstudio/express/visualc/ - - -BUILDING --------- - -Make sure all the needed tools (compiler, swig) are on the PATH. - -1, Unpack the IDAPython source and IDA Pro SDK into the following - directory structure: - - swigsdk-versions/5.6/ - version 5.6 of the IDA Pro SDK - idapython/ - IDAPython source code - -2, On Mac OS X copy libida.dylib from the IDA install directory to - swigsdk-versions/5.6/lib/gcc32.mac/ - and libida64.dylib to - swigsdk-versions/5.6/lib/gcc64.mac/ - -3, Build the plugin - - python build.py - - It is possible to build the plugin for different Python versions by - running build.py with the corresponding Python binary. - - Some build options: - --ea64: builds the 64-bit version - --no-early-load: builds the IDAPython plugin w/o PLUGIN_FIX plugin flag - (This flag disables the ability to write file loaders in IDAPython) - -4, Install the components as described in README.txt - -See build.py for build details and possible tweaks. +---------------------------------------------------------- +IDAPython - Python plugin for Interactive Disassembler Pro +---------------------------------------------------------- +Building From Source +-------------------- + +REQUIREMENTS +------------ + +[Tested versions are in brackets] + + - IDA and IDA SDK [5.6] + http://www.hex-rays.com/idapro/ + + - Python [2.5.1, 2.6.1] + http://www.python.org/ + + - Simplified Wrapper Interface Generator (SWIG) [1.3.36] + http://www.swig.org/ + + - Unix utilities (GNU patch on Windows): + http://www.research.att.com/sw/tools/uwin/ or + http://unxutils.sourceforge.net/ or + http://www.cygwin.com/ + + - GCC on Linux and Mac OS X [4.0.1, 4.1.3] + Comes with your distribution + + - Microsoft Visual C on Windows [Microsoft Visual C++ 2008 Express Edition] + http://msdn.microsoft.com/vstudio/express/visualc/ + + +BUILDING +-------- + +Make sure all the needed tools (compiler, swig) are on the PATH. + +1, Unpack the IDAPython source and IDA Pro SDK into the following + directory structure: + + swigsdk-versions/5.6/ - version 5.6 of the IDA Pro SDK + idapython/ - IDAPython source code + +2, On Mac OS X copy libida.dylib from the IDA install directory to + swigsdk-versions/5.6/lib/gcc32.mac/ + and libida64.dylib to + swigsdk-versions/5.6/lib/gcc64.mac/ + +3, Build the plugin + + python build.py + + It is possible to build the plugin for different Python versions by + running build.py with the corresponding Python binary. + + Run 'build.py --help' for more information. + +4, Install the components as described in README.txt + +See build.py for build details and possible tweaks. diff --git a/CHANGES.txt b/CHANGES.txt index 6b338ea..2c311cf 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,105 +1,117 @@ -Please see http://code.google.com/p/idapython/source/list for a -detailed list of changes. - -Changes from version 1.2.0 to 1.3.0 ------------------------------------- - -- IDA Pro 5.6 support -- Added Appcall mechanism -- Added procregs to idautils.py (r254) -- Lots of cleanups and fixes - -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 -- Host of IDC updates and fixes -- netnode.hpp is now mostly wrapped -- idautils use generators instead of lists -- Functions() and GetFchunkAttr() now work properly -- Lots of cleanups and fixes - - -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 -- IDC compatibility layer is now complete and up to date for IDA 5.1 -- INCOMPATIBLE CHANGE: the idaapi module needs to be imported manually -- Support for IDB and debug notification hooks -- Support for GUI hotkeys (see examples/hotkey.py) -- Simple two-way calling mechanism between IDC and IDAPython -- Significantly better IDA API coverage -- Support for IDB and debug event hooks -- get_current_instruction() deprecated, use the idaapi.cvar.cmd variable -- Tons of IDC fixes -- Tons of other misc fixes - - -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 -- Close to complete IDC compatbility layer (in sync with 4.9) -- Significatnly improved IDA SDK API covergage (see STATUS.txt for details) -- IDA SDK patch size reduced to less than half -- Simplified installation (plugins.cfg modification not needed) -- Evaluation window content is saved over IDA restarts (in the database) -- Windows version is built with Microsoft Visual C++ Express Edition -- Build makefile replaced with a Python script -- Cleanups and small fixes - - -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. -- New wrappers: search.hpp, dbg.hpp, loader.hpp, diskio.hpp, nalt.hpp -- idc.py synced up to IDA 4.8 -- Added 38 IDC functions -- Fixed asklong(), askseg() and askaddr() -- Automatically generated cross reference documentation (epydoc) -- User-specific init file support (see README,txt) -- Deprecated some functions that have direct Python equivalents (see idc.py) -- Fixed exception in ScriptBox when invoked empty. -- Lots of cleanups and small fixes - - -Changes from version 0.6.0 to 0.7.0 ------------------------------------ -- Batch execution support (use the option -OIDAPython:yourscript.py) -- Added ScriptBox - lists previously run scripts (Hotkey:Alt-7) -- Added support for IDA Pro 4.8 (both Linux and Windows) -- Dropped support for IDA Pro 4.6 and 4.6SP1 versions -- Wrapped the list chooser (see examples/choose.py) -- A dozen or so IDC functions added -- Lots of char * API calls wrapped -- Added Python error handling in the plugin C layer -- Bunch of misc small cleanups and fixes -- For more details see CHANGES-SWIG.txt and CHANGES-Plugin.txt - -- API CHANGE: {Next|Prev}Function() return BADADDR instead of -1 - - -Changes from version 0.5.0 to 0.6.0 ------------------------------------ -- Added support for IDA Pro 4.7 (both Linux and Windows) -- Dropped support for IDA Pro 4.6SP1 beta on Linux -- Lots of IDC wrapper additions and fixes: - - Added 30+ new wrappers to idc.py - - Most Find*, Ask* and Seg* are now wrapped - - Fixed broken NextAddr(), PrevAddr(), MakeFunction() and MakeName() -- Fixes to the makefile -- Cleanups for the idaapi wrapper -- Bunch of misc small cleanups and fixes -- For more details see CHANGES-SWIG.txt and CHANGES-Plugin.txt - - +Please see http://code.google.com/p/idapython/source/list for a detailed list of changes. + +Changes from version 1.3.0 to 1.4.0 +------------------------------------ +- IDA Pro 5.7 support +- idaapi.cvar.cmd is now accessible via idapi.cmd instead +- Python statement (Alt-8) is now 16kb long +- Dropped script box and File/Python file. IDA has this functionality now. +- Refactored the code +- It is possible to turn off script timeout +- All scripts are executed via 'IDAPython_ExecScript' (check idaapi.i) +- Added '--doc' switch to "build.py" script +- 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) +- Lots of cleanups and fixes + +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 +- Host of IDC updates and fixes +- netnode.hpp is now mostly wrapped +- idautils use generators instead of lists +- Functions() and GetFchunkAttr() now work properly +- Lots of cleanups and fixes + + +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 +- IDC compatibility layer is now complete and up to date for IDA 5.1 +- INCOMPATIBLE CHANGE: the idaapi module needs to be imported manually +- Support for IDB and debug notification hooks +- Support for GUI hotkeys (see examples/hotkey.py) +- Simple two-way calling mechanism between IDC and IDAPython +- Significantly better IDA API coverage +- Support for IDB and debug event hooks +- get_current_instruction() deprecated, use the idaapi.cvar.cmd variable +- Tons of IDC fixes +- Tons of other misc fixes + + +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 +- Close to complete IDC compatbility layer (in sync with 4.9) +- Significatnly improved IDA SDK API covergage (see STATUS.txt for details) +- IDA SDK patch size reduced to less than half +- Simplified installation (plugins.cfg modification not needed) +- Evaluation window content is saved over IDA restarts (in the database) +- Windows version is built with Microsoft Visual C++ Express Edition +- Build makefile replaced with a Python script +- Cleanups and small fixes + + +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. +- New wrappers: search.hpp, dbg.hpp, loader.hpp, diskio.hpp, nalt.hpp +- idc.py synced up to IDA 4.8 +- Added 38 IDC functions +- Fixed asklong(), askseg() and askaddr() +- Automatically generated cross reference documentation (epydoc) +- User-specific init file support (see README,txt) +- Deprecated some functions that have direct Python equivalents (see idc.py) +- Fixed exception in ScriptBox when invoked empty. +- Lots of cleanups and small fixes + + +Changes from version 0.6.0 to 0.7.0 +----------------------------------- +- Batch execution support (use the option -OIDAPython:yourscript.py) +- Added ScriptBox - lists previously run scripts (Hotkey:Alt-7) +- Added support for IDA Pro 4.8 (both Linux and Windows) +- Dropped support for IDA Pro 4.6 and 4.6SP1 versions +- Wrapped the list chooser (see examples/choose.py) +- A dozen or so IDC functions added +- Lots of char * API calls wrapped +- Added Python error handling in the plugin C layer +- Bunch of misc small cleanups and fixes +- For more details see CHANGES-SWIG.txt and CHANGES-Plugin.txt + +- API CHANGE: {Next|Prev}Function() return BADADDR instead of -1 + + +Changes from version 0.5.0 to 0.6.0 +----------------------------------- +- Added support for IDA Pro 4.7 (both Linux and Windows) +- Dropped support for IDA Pro 4.6SP1 beta on Linux +- Lots of IDC wrapper additions and fixes: + - Added 30+ new wrappers to idc.py + - Most Find*, Ask* and Seg* are now wrapped + - Fixed broken NextAddr(), PrevAddr(), MakeFunction() and MakeName() +- Fixes to the makefile +- Cleanups for the idaapi wrapper +- Bunch of misc small cleanups and fixes +- For more details see CHANGES-SWIG.txt and CHANGES-Plugin.txt + + diff --git a/COPYING.txt b/COPYING.txt index 61844a2..00c0adb 100644 --- a/COPYING.txt +++ b/COPYING.txt @@ -1,27 +1,27 @@ -Copyright (c) 2004-2010 Gergely Erdelyi . All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -3. The name of the author may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY -OF SUCH DAMAGE. - - +Copyright (c) 2004-2010 Gergely Erdelyi . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + + diff --git a/README.txt b/README.txt index b8c5b9a..55af21a 100644 --- a/README.txt +++ b/README.txt @@ -1,86 +1,86 @@ ----------------------------------------------------------- -IDAPython - Python plugin for Interactive Disassembler Pro ----------------------------------------------------------- - -WHAT IS IDAPTYHON? ------------------- - -IDAPython is an IDA plugin which makes it possible to write scripts -for IDA in the Python programming language. IDAPython provides full -access to both the IDA API and any installed Python module. - -Check the scripts in the examples directory to get an quick glimpse. - - -AVAILABILITY ------------- - -Latest stable versions of IDAPython are available from - http://www.d-dome.net/idapython/ - -Development builds are available from - http://code.google.com/p/idapython/ - - -RESOURCES ---------- - -The full function cross-reference is readable online at - http://www.d-dome.net/idapython/reference/ - -Bugs and enhancement requests should be submitted to - http://code.google.com/p/idapython/issues/list - -Mailing list for the project is hosted by Google Groups at - http://groups.google.com/group/idapython - - -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\ - - -USAGE ------ - -The plugin has three hotkeys: - - - Run script (Alt-9) - - Execute Python statement(s) (Alt-8) - - Run previously executed script again (Alt-7) - -Batch mode execution: - -Start IDA with the following command line options: - - -A -OIDAPython:yourscript.py file_to_work_on - -If you want fully unattended execution mode, make sure your script -exits with a qexit() call. - -By default scripts run after the database is opened. Extended option -format is: - - -OIDAPython:[N;]script.py - -Where N can be: - 0: run script after opening database (default) - 1: run script when UI is ready - 2: run script immediately on plugin load (shortly after IDA starts and before processor modules and loaders) - -User init file: - -You can place your custom settings to a file called 'idapythonrc.py' -that should be placed to - -${HOME}/.idapro/ - -or - -%AppData%\Hex-Rays\IDA Pro - -The user init file is read and executed at the end of the init process. - +---------------------------------------------------------- +IDAPython - Python plugin for Interactive Disassembler Pro +---------------------------------------------------------- + +WHAT IS IDAPTYHON? +------------------ + +IDAPython is an IDA plugin which makes it possible to write scripts +for IDA in the Python programming language. IDAPython provides full +access to both the IDA API and any installed Python module. + +Check the scripts in the examples directory to get an quick glimpse. + + +AVAILABILITY +------------ + +Latest stable versions of IDAPython are available from + http://www.d-dome.net/idapython/ + +Development builds are available from + http://code.google.com/p/idapython/ + + +RESOURCES +--------- + +The full function cross-reference is readable online at + http://www.d-dome.net/idapython/reference/ + +Bugs and enhancement requests should be submitted to + http://code.google.com/p/idapython/issues/list + +Mailing list for the project is hosted by Google Groups at + http://groups.google.com/group/idapython + + +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\ + + +USAGE +----- + +The plugin has three hotkeys: + + - Run script (Alt-9) + - Execute Python statement(s) (Alt-8) + - Run previously executed script again (Alt-7) + +Batch mode execution: + +Start IDA with the following command line options: + + -A -OIDAPython:yourscript.py file_to_work_on + +If you want fully unattended execution mode, make sure your script +exits with a qexit() call. + +By default scripts run after the database is opened. Extended option +format is: + + -OIDAPython:[N;]script.py + +Where N can be: + 0: run script after opening database (default) + 1: run script when UI is ready + 2: run script immediately on plugin load (shortly after IDA starts and before processor modules and loaders) + +User init file: + +You can place your custom settings to a file called 'idapythonrc.py' +that should be placed to + +${HOME}/.idapro/ + +or + +%AppData%\Hex-Rays\IDA Pro + +The user init file is read and executed at the end of the init process. + diff --git a/STATUS.txt b/STATUS.txt index dd2d396..2fbb834 100644 --- a/STATUS.txt +++ b/STATUS.txt @@ -1,66 +1,66 @@ -Status of the IDC layer ------------------------ - -The IDC emulation layer is complete and at par with IDA 5.1, -although it would benefit from more testing. - - -Status of IDA API wrappers --------------------------- - -COMPLETE: all possible functions wrapped, no SWIG ifdefs -INCOMPLETE: some wrapping or SWIG ifdefs still left -EXCLUDED: will not be wrapped - -allins.hpp - COMPLETE -area.hpp - COMPLETE (necessary SWIGdefs) -auto.hpp - COMPLETE -bytes.hpp - COMPLETE (some minor unwrapped) -compress.hpp - EXCLUDED -dbg.hpp - INCOMPLETE (SWIGs and lot of fixing to do) -demangle.hpp - EXCLUDED -diskio.hpp - INCOMPLETE (no SWIGs, some unwrapped) -entry.hpp - COMPLETE -enum.hpp - COMPLETE -err.h - EXCLUDED -exehdr.h - EXCLUDED -expr.hpp - COMPLETE (necessary SWIGs) -fixup.hpp - COMPLETE -fpro.h - EXCLUDED -frame.hpp - COMPLETE -funcs.hpp - COMPLETE (necessary SWIGs, minor FIXME) -gdl.hpp - EXCLUDED -graph.hpp - INCOMPLETE -help.h - EXCLUDED -ida.hpp - COMPLETE -idd.hpp - COMPLETE (necessary SWIGs) -idp.hpp - COMPLETE -ieee.h - EXCLUDED -intel.hpp - EXCLUDED -ints.hpp - COMPLETE -kernwin.hpp - INCOMPLETE (SWIGs and lot of fixing to do) -lex.hpp - EXCLUDED -lines.hpp - INCOMPLETE (few FIXMEs) -llong.hpp - EXCLUDED -loader.hpp - INCOMPLETE (few FIXMEs) -md5.h - EXCLUDED -moves.hpp - COMPLETE (some needed SWIGs) -nalt.hpp - INCOMPLETE (SWIGs and lot of fixing to do) -name.hpp - INCOMPLETE (few FIXMEs) -netnode.hpp - COMPLETE -offset.hpp - COMPLETE -prodir.h - EXCLUDED -pro.h - COMPLETE (some needed SWIGs) -queue.hpp - INCOMPLETE (one FIXME) -regex.h - EXCLUDED -search.hpp - COMPLETE -segment.hpp - COMPLETE -sistack.hpp - EXCLUDED -srarea.hpp - INCOMPLETE (not wrapped at all) -strlist.hpp - COMPLETE -struct.hpp - COMPLETE -typeinf.hpp - INCOMPLETE (no SWIGs, lot of fixing to do) -ua.hpp - INCOMPLETE (SWIGs and lot of fixing to do) -va.hpp - EXCLUDED -vm.hpp - EXCLUDED -xref.hpp - COMPLETE +Status of the IDC layer +----------------------- + +The IDC emulation layer is complete and at par with IDA 5.1, +although it would benefit from more testing. + + +Status of IDA API wrappers +-------------------------- + +COMPLETE: all possible functions wrapped, no SWIG ifdefs +INCOMPLETE: some wrapping or SWIG ifdefs still left +EXCLUDED: will not be wrapped + +allins.hpp - COMPLETE +area.hpp - COMPLETE (necessary SWIGdefs) +auto.hpp - COMPLETE +bytes.hpp - COMPLETE (some minor unwrapped) +compress.hpp - EXCLUDED +dbg.hpp - INCOMPLETE (SWIGs and lot of fixing to do) +demangle.hpp - EXCLUDED +diskio.hpp - INCOMPLETE (no SWIGs, some unwrapped) +entry.hpp - COMPLETE +enum.hpp - COMPLETE +err.h - EXCLUDED +exehdr.h - EXCLUDED +expr.hpp - COMPLETE (necessary SWIGs) +fixup.hpp - COMPLETE +fpro.h - EXCLUDED +frame.hpp - COMPLETE +funcs.hpp - COMPLETE (necessary SWIGs, minor FIXME) +gdl.hpp - EXCLUDED +graph.hpp - INCOMPLETE +help.h - EXCLUDED +ida.hpp - COMPLETE +idd.hpp - COMPLETE (necessary SWIGs) +idp.hpp - COMPLETE +ieee.h - EXCLUDED +intel.hpp - EXCLUDED +ints.hpp - COMPLETE +kernwin.hpp - INCOMPLETE (SWIGs and lot of fixing to do) +lex.hpp - EXCLUDED +lines.hpp - INCOMPLETE (few FIXMEs) +llong.hpp - EXCLUDED +loader.hpp - INCOMPLETE (few FIXMEs) +md5.h - EXCLUDED +moves.hpp - COMPLETE (some needed SWIGs) +nalt.hpp - INCOMPLETE (SWIGs and lot of fixing to do) +name.hpp - INCOMPLETE (few FIXMEs) +netnode.hpp - COMPLETE +offset.hpp - COMPLETE +prodir.h - EXCLUDED +pro.h - COMPLETE (some needed SWIGs) +queue.hpp - INCOMPLETE (one FIXME) +regex.h - EXCLUDED +search.hpp - COMPLETE +segment.hpp - COMPLETE +sistack.hpp - EXCLUDED +srarea.hpp - INCOMPLETE (not wrapped at all) +strlist.hpp - COMPLETE +struct.hpp - COMPLETE +typeinf.hpp - INCOMPLETE (no SWIGs, lot of fixing to do) +ua.hpp - INCOMPLETE (SWIGs and lot of fixing to do) +va.hpp - EXCLUDED +vm.hpp - EXCLUDED +xref.hpp - COMPLETE diff --git a/build.py b/build.py index 8760624..b79bad7 100644 --- a/build.py +++ b/build.py @@ -17,12 +17,15 @@ import shutil import sys import types import zipfile +import glob from distutils import sysconfig # Start of user configurable options VERBOSE = True + IDA_MAJOR_VERSION = 5 -IDA_MINOR_VERSION = 6 +IDA_MINOR_VERSION = 7 + if 'IDA' in os.environ: IDA_SDK = os.environ['IDA'] else: @@ -32,8 +35,8 @@ else: # IDAPython version VERSION_MAJOR = 1 -VERSION_MINOR = 3 -VERSION_PATCH = 2 +VERSION_MINOR = 4 +VERSION_PATCH = 0 # Determine Python version PYTHON_MAJOR_VERSION = int(platform.python_version()[0]) @@ -43,7 +46,7 @@ PYTHON_MINOR_VERSION = int(platform.python_version()[2]) PYTHON_INCLUDE_DIRECTORY = sysconfig.get_config_var('INCLUDEPY') # Swig command-line parameters -SWIG_OPTIONS = '-modern -python -c++ -w451 -shadow -D__GNUC__' +SWIG_OPTIONS = '-modern -python -c++ -w451 -shadow -D__GNUC__ -DNO_OBSOLETE_FUNCS' # Common macros for all compilations COMMON_MACROS = [ @@ -75,6 +78,7 @@ BINDIST_MANIFEST = [ "examples/structure.py", "examples/ex_gdl_qflow_chart.py", "examples/ex_strings.py", + "examples/ex_add_menu_item.py", "examples/ex_func_chooser.py", "examples/ex_choose2.py", "examples/ex_debug_names.py", @@ -162,7 +166,8 @@ class BuilderBase: includestring, macrostring) - if VERBOSE: print cmdstring + if VERBOSE: + print cmdstring return os.system(cmdstring) def link(self, objects, outfile, libpaths=[], libraries=[], extra_parameters=None): @@ -413,7 +418,73 @@ def build_source_package(): srcmanifest.extend([(x, "python") for x in "python/init.py", "python/idc.py", "python/idautils.py"]) build_distribution(srcmanifest, SRCDISTDIR, ea64=False, nukeold=True) +def gen_docs(z = False): + print "Generating documentation....." + old_dir = os.getcwd() + try: + curdir = os.getcwd() + os.sep + docdir = 'idapython-reference-%d.%d.%d' % (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) + sys.path.append(curdir + 'python') + sys.path.append(curdir + 'tools') + sys.path.append(curdir + 'docs') + import epydoc.cli + import swigdocs + os.chdir('docs') + PYWRAPS_FN = 'pywraps' + swigdocs.gen_docs(outfn = PYWRAPS_FN + '.py') + epydoc.cli.optparse.sys.argv = [ 'epydoc', + '--no-sourcecode', + '-u', 'http://code.google.com/p/idapython/', + '--navlink', 'IDAPython Reference', + '--no-private', + '--simple-term', + '-o', docdir, + '--html', + 'idc', 'idautils', PYWRAPS_FN, 'idaapi'] + # Generate the documentation + epydoc.cli.cli() + + print "Documentation generated!" + + # Clean temp files + for f in [PYWRAPS_FN + '.py', PYWRAPS_FN + '.pyc']: + if os.path.exists(f): + os.unlink(f) + + if z: + z = docdir + '-doc.zip' + zip = zipfile.ZipFile(z, "w", zipfile.ZIP_DEFLATED) + for fn in glob.glob(docdir + os.sep + '*'): + zip.write(fn) + zip.close() + print "Documentation compressed to", z + except Exception, e: + print 'Failed to generate documentation:', e + finally: + os.chdir(old_dir) + return + +def usage(): + print """IDAPython build script. + +Available switches: + --doc: + Generate documentation into the 'docs' directory + --zip: + Used with '--doc' switch. It will compress the generated documentation + --ea64: + Builds also the 64bit version of the plugin + --no-early-load: + The plugin will be compiled as normal plugin + This switch disables processor, plugin and loader scripts +""" + def main(): + if '--help' in sys.argv: + return usage() + elif '--doc' in sys.argv: + return gen_docs(z = '--zip' in sys.argv) + # Do 64-bit build? ea64 = '--ea64' in sys.argv build_binary_package(ea64=False, nukeold=True) @@ -422,4 +493,4 @@ def main(): build_source_package() if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/examples/ex_add_menu_item.py b/examples/ex_add_menu_item.py new file mode 100644 index 0000000..0b681bc --- /dev/null +++ b/examples/ex_add_menu_item.py @@ -0,0 +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 diff --git a/examples/ex_custview.py b/examples/ex_custview.py index 9b373a8..eb8de0f 100644 --- a/examples/ex_custview.py +++ b/examples/ex_custview.py @@ -15,7 +15,7 @@ class mycv_t(simplecustviewer_t): if sn: title += " %d" % sn - # Create the customview + # Create the customviewer if not simplecustviewer_t.Create(self, title): return False self.menu_hello = self.AddPopupMenu("Hello") @@ -24,6 +24,8 @@ class mycv_t(simplecustviewer_t): for i in xrange(0, 100): self.AddLine("Line %d" % i) +# self.Jump(0) + return True def OnClick(self, shift): @@ -71,6 +73,7 @@ class mycv_t(simplecustviewer_t): # ESCAPE? if vkey == 27: self.Close() + # VK_DELETE elif vkey == 46: n = self.GetLineNo() if n is not None: @@ -129,10 +132,10 @@ class mycv_t(simplecustviewer_t): Hint requested for the given line number. @param lineno: The line number (zero based) @return: - - string: a string containing the hint + - tuple(number of important lines, hint string) - None: if no hint available """ - return "OnHint, line=%d" % lineno + return (1, "OnHint, line=%d" % lineno) def OnPopupMenu(self, menu_id): """ diff --git a/python.cpp b/python.cpp index d37fda0..1f4b5a0 100644 --- a/python.cpp +++ b/python.cpp @@ -1,1080 +1,1355 @@ -//--------------------------------------------------------------------- -// 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. -//--------------------------------------------------------------------- -// python.cpp - Main plugin code -//--------------------------------------------------------------------- -#include - -/* This define fixes the redefinition of ssize_t */ -#ifdef HAVE_SSIZE_T -#define _SSIZE_T_DEFINED 1 -#endif - -#include -#include -#ifdef __LINUX__ -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" -#endif - -/* Python-style version tuple comes from the makefile */ -/* Only the serial and status is set here */ -#define VER_SERIAL 0 -#define VER_STATUS "final" - -#define IDAPYTHON_RUNFILE 0 -#define IDAPYTHON_RUNSTATEMENT 1 -#define IDAPYTHON_SCRIPTBOX 2 - - -#define IDAPYTHON_DATA_STATEMENT 0 - -#define PYTHON_DIR_NAME "python" - -void init_idaapi(void); -void idaapi run(int arg); -static int initialized = 0; -static bool menu_installed = false; -//-------------------------------------------------------------------------- -// Some utility functions from pywraps / idaapi -int idcvar_to_pyvar(const idc_value_t &idc_var, PyObject **py_var); -int pyvar_to_idcvar(PyObject *py_var, idc_value_t *idc_var, int *gvar_sn = NULL); -PyObject *PyObject_TryGetAttrString(PyObject *py_var, const char *attr); -PyObject *PyImport_TryImportModule(const char *name); -bool PyGetError(qstring *out = NULL); -bool init_pywraps(); -void deinit_pywraps(); -static const char S_MAIN[] = "__main__"; -//-------------------------------------------------------------------------- - -/* This is a simple tracing code for debugging purposes. */ -/* It might evolve into a tracing facility for user scripts. */ -/* #define ENABLE_PYTHON_PROFILING */ - -#ifdef ENABLE_PYTHON_PROFILING -#include "compile.h" -#include "frameobject.h" - -int tracefunc(PyObject *obj, _frame *frame, int what, PyObject *arg) -{ - PyObject *str; - - /* Catch line change events. */ - /* Print the filename and line number */ - if (what == PyTrace_LINE) - { - str = PyObject_Str(frame->f_code->co_filename); - if (str) - { - msg("PROFILING: %s:%d\n", PyString_AsString(str), frame->f_lineno); - Py_DECREF(str); - } - } - return 0; -} -#endif - -/* Helper routines to make Python script execution breakable from IDA */ -static int ninsns = 0; // number of times trace function was called -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; - -/* Exported to the Python environment */ -void set_script_timeout(int timeout) -{ - script_timeout = timeout; -} - -/* This callback is called on various interpreter events */ -int break_check(PyObject *obj, _frame *frame, int what, PyObject *arg) -{ - if (wasBreak()) - /* 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 */ - ninsns = 0; - if (time(NULL) - start_time > script_timeout) /* Timeout elapsed? */ - { - show_wait_box("Running Python script"); - box_displayed = true; - } - } -#ifdef ENABLE_PYTHON_PROFILING - return tracefunc(obj, frame, what, arg); -#else - return 0; -#endif -} - -/* Prepare for Python execution */ -void begin_execution() -{ - ninsns = 0; - box_displayed = false; - start_time = time(NULL); - PyEval_SetTrace(break_check, NULL); -} - -/* Called after Python execution finishes */ -void end_execution() -{ - if (box_displayed) - hide_wait_box(); -#ifdef ENABLE_PYTHON_PROFILING - PyEval_SetTrace(tracefunc, NULL); -#else - PyEval_SetTrace(NULL, NULL); -#endif -} - -/* Return a formatted error or just print it to the console */ -static void handle_python_error(char *errbuf, size_t errbufsize) -{ - PyObject *result; - PyObject *ptype, *pvalue, *ptraceback; - - if ( errbufsize > 0 ) - errbuf[0] = '\0'; - - if (PyErr_Occurred()) - { - PyErr_Fetch(&ptype, &pvalue, &ptraceback); - result = PyObject_Repr(pvalue); - if (result) - { - qsnprintf(errbuf, errbufsize, "ERROR: %s", PyString_AsString(result)); - PyErr_Clear(); - Py_XDECREF(ptype); - Py_XDECREF(pvalue); - Py_XDECREF(ptraceback); - Py_DECREF(result); - } - else - PyErr_Print(); - } -} - -/* helper function to get globals for the __main__ module */ -PyObject *GetMainGlobals() -{ - PyObject *module = PyImport_AddModule(S_MAIN); - if (module == NULL) - return NULL; - - return PyModule_GetDict(module); -} - -/* Simple Python statement runner function for IDC */ -static const char idc_runpythonstatement_args[] = { VT_STR2, 0 }; -static error_t idaapi idc_runpythonstatement(idc_value_t *argv, idc_value_t *res) -{ - PyObject *globals = GetMainGlobals(); - if (globals == NULL) - { - res->set_string("internal error"); - } - else - { - PyErr_Clear(); - begin_execution(); - PyObject *result = PyRun_String(argv[0].c_str(), Py_file_input, globals, globals ); - end_execution(); - Py_XDECREF(result); - if ( result == NULL || PyErr_Occurred() ) - { - char errbuf[MAXSTR]; - handle_python_error(errbuf, sizeof(errbuf)); - *res = idc_value_t(errbuf); - if ( errbuf[0] == '\0' ) - res->set_string("internal error"); - else - res->set_string(errbuf); - } - else - { - // success - res->set_long(0); - } - } - return eOk; -} - -/* QuickFix for the FILE* incompatibility problem */ -int ExecFile(const char *FileName) -{ - PyObject *PyFileObject = PyFile_FromString((char*)FileName, "r"); - - PyObject *globals = GetMainGlobals(); - if (globals == NULL) - return 0; - - PyErr_Clear(); - PyObject *result = PyRun_File(PyFile_AsFile(PyFileObject), FileName, Py_file_input, globals, globals); - - Py_XDECREF(PyFileObject); - Py_XDECREF(result); - if ( result == NULL || PyErr_Occurred() ) - { - if ( !PyErr_Occurred() ) - PyErr_Print(); - return 0; - } - - return 1; -} - -/* Check for the presence of a file in IDADIR/python */ -bool CheckFile(char *filename) -{ - char filepath[MAXSTR+1]; - - qmakepath(filepath, MAXSTR, idadir(PYTHON_DIR_NAME), filename, NULL); - if (!qfileexist(filepath)) - { - warning("IDAPython: Missing required file %s", filename); - return false; - } - - return true; -} - -/* Execute the Python script from the plugin */ -/* Default hotkey: Alt-9 */ -void IDAPython_RunScript(const char *script) -{ - char statement[MAXSTR+32]; - char slashpath[MAXSTR+1]; - const char *scriptpath; - - int i; - - if (script) - scriptpath = script; - else - { - scriptpath = askfile_c(0, "*.py", "Python file to run"); - if (!scriptpath) - return; - } - - /* Make a copy of the path with '\\' => '/' */ - for (i=0; scriptpath[i]; i++) - { - if (scriptpath[i] == '\\') - slashpath[i] = '/'; - else - slashpath[i] = scriptpath[i]; - } - slashpath[i] = '\0'; - - /* Add the script't path to sys.path */ - qsnprintf(statement, sizeof(statement), "runscript(\"%s\")", slashpath); - begin_execution(); - PyRun_SimpleString(statement); - end_execution(); - - /* Error handling */ - if (PyErr_Occurred()) - PyErr_Print(); - -} - -/* Execute Python statement(s) from an editor window */ -/* Default hotkey: Alt-8 */ -void IDAPython_RunStatement(void) -{ - char statement[4096]; - netnode history; - - /* Get the existing or create a new netnode in the database */ - history.create("IDAPython_Data"); - - /* Fetch the previous statement */ - if (history.supval(IDAPYTHON_DATA_STATEMENT, statement, sizeof(statement)) == -1) - statement[0] = '\0'; - - if (asktext(sizeof(statement), statement, statement, "Enter Python expressions")) - { - begin_execution(); - PyRun_SimpleString(statement); - end_execution(); - /* Store the statement to the database */ - history.supset(IDAPYTHON_DATA_STATEMENT, statement); - } -} - -/* History of previously executed scripts */ -/* Default hotkey: Alt-7 */ -void IDAPython_ScriptBox(void) -{ - PyObject *dict; - PyObject *scriptbox; - PyObject *pystr; - - /* Get globals() */ - /* This should never fail */ - dict = GetMainGlobals(); - - scriptbox = PyDict_GetItemString(dict, "scriptbox"); - - if (!scriptbox) - { - warning("INTERNAL ERROR: ScriptBox_instance missing! Broken init.py?"); - return; - } - - pystr = PyObject_CallMethod(scriptbox, "run", ""); - - if (!pystr) - { - /* Print the exception info */ - if (PyErr_Occurred()) - PyErr_Print(); - } -} - -bool idaapi IDAPython_Menu_Callback(void *ud) -{ - run((size_t)ud); - return true; -} - - -//-------------------------------------------------------------------------- -// This function parses a name into two different components (if it applies). -// Example: -// parse_py_modname("modname.attrname", mod_buf, attr_buf) -// It splits the full name into two parts. -static bool parse_py_modname( - const char *full_name, - char *modname, - char *attrname, - size_t sz, - const char *defmod = "idaapi") -{ - const char *p = strchr(full_name, '.'); - if (p == NULL) - { - qstrncpy(modname, defmod, sz); - qstrncpy(attrname, full_name, sz); - } - else - { - qstrncpy(modname, full_name, p - full_name + 1); - qstrncpy(attrname, p + 1, sz); - } - return p != NULL; -} - -/* Convert return value from Python to IDC or report about an error. */ -/* This function also decrements the reference "result" (python variable) */ -static bool return_python_result(idc_value_t *rv, - PyObject *result, - char *errbuf, - size_t errbufsize) -{ - if (errbufsize > 0) - errbuf[0] = '\0'; - - if (result == NULL) - { - handle_python_error(errbuf, errbufsize); - return false; - } - bool ok = true; - if (pyvar_to_idcvar(result, rv) <= 0) - { - qsnprintf(errbuf, errbufsize, "ERROR: bad return value"); - ok = false; - } - Py_XDECREF(result); - return ok; -} - -/* Compile callback for Python external language evaluator */ -bool idaapi IDAPython_extlang_compile(const char *name, - ea_t /*current_ea*/, - const char *expr, - char *errbuf, - size_t errbufsize) -{ - PyObject *globals = GetMainGlobals(); QASSERT(globals != NULL); - - PyCodeObject *code = (PyCodeObject *)Py_CompileString(expr, "", Py_eval_input); - if (code == NULL) - { - handle_python_error(errbuf, errbufsize); - return false; - } - - // set the desired function name - Py_XDECREF(code->co_name); - code->co_name = PyString_FromString(name); - - // create a function out of code - PyObject *func = PyFunction_New((PyObject *)code, globals); - if (func == NULL) - { - ERR: - handle_python_error(errbuf, errbufsize); - Py_XDECREF(code); - return false; - } - - int err = PyDict_SetItemString(globals, name, func); - if (err) - goto ERR; - - return true; -} - -/* Run callback for Python external language evaluator */ -bool idaapi IDAPython_extlang_run(const char *name, - int nargs, - const idc_value_t args[], - idc_value_t *result, - char *errbuf, - size_t errbufsize) -{ - // convert arguments to python - qvector pargs; - qvector do_decref; - - bool ok = true; - - PyObject *module(NULL); - char modname[MAXSTR] = {0}; - char funcname[MAXSTR] = {0}; - bool imported_module = parse_py_modname(name, modname, funcname, MAXSTR); - do - { - for (int i=0; i 0; - // decrement reference only if not an opaque object - if (r == 1) - Py_DECREF(py_res); - } while (false); - - Py_XDECREF(py_mod); - Py_XDECREF(py_cls); - - // Free the arguments tuple - if (py_args != NULL) - Py_DECREF(py_args); - - return ok; -} - -// Returns: success - -/* Calculator callback for Python external language evaluator */ -bool idaapi IDAPython_extlang_calcexpr(ea_t /*current_ea*/, - const char *expr, - idc_value_t *rv, - char *errbuf, - size_t errbufsize) -{ - PyObject *result; - - PyObject *globals = GetMainGlobals(); - if (globals == NULL) - return false; - - begin_execution(); - result = PyRun_String(expr, Py_eval_input, globals, globals); - end_execution(); - - rv->clear(); - - return return_python_result(rv, result, errbuf, errbufsize); -} - -extlang_t extlang_python = -{ - sizeof(extlang_t), - 0, - "Python", - IDAPython_extlang_compile, - IDAPython_extlang_run, - IDAPython_extlang_calcexpr, - IDAPython_extlang_compile_file, - "py", - IDAPython_extlang_create_object -}; - -void enable_extlang_python(bool enable) -{ -#if IDA_SDK_VERSION < 560 -#define SELECT_EXTLANG register_extlang -#else -#define SELECT_EXTLANG select_extlang -#endif - if (enable) - SELECT_EXTLANG(&extlang_python); - else - SELECT_EXTLANG(NULL); -#undef SELECT_EXTLANG -} - -#if IDA_SDK_VERSION >= 540 -/* Execute a line in the Python CLI */ -bool idaapi IDAPython_cli_execute_line(const char *line) -{ - const char *first_line = strrchr(line, '\n'); - if (first_line == NULL) - first_line = line; - else - first_line += 1; - - // skip empty lines - if (first_line[0] != '\0') - { - // take a copy of the line so we r-trim it - char *tline = qstrdup(first_line); - trim(tline); - // line ends with ":" or begins with a space character? - bool more = tline[qstrlen(tline)-1] == ':' || isspace(first_line[0]); - qfree(tline); - - if ( more ) - return false; - } - begin_execution(); - PyRun_SimpleString(line); - end_execution(); - - return true; -} - -cli_t cli_python = -{ - sizeof(cli_t), - 0, - "Python", - "Python - IDAPython plugin", - "Enter any Python expression", - IDAPython_cli_execute_line, - NULL, - NULL -}; - -/* Control the Python CLI status */ -void enable_python_cli(bool enable) -{ - if (enable) - install_command_interpreter(&cli_python); - else - remove_command_interpreter(&cli_python); -} -#endif - -/* Prints the IDAPython copyright banner */ -void print_banner() -{ - PyRun_SimpleString("print_banner()"); -} - -/* Install python menu items */ -static void install_python_menus() -{ - if (menu_installed) - return; - - /* Add menu items for all the functions */ - /* Different paths are used for the GUI version */ - add_menu_item("File/IDC command...", "P~y~thon command...", - "Alt-8", SETMENU_APP, - (menu_item_callback_t *)IDAPython_Menu_Callback, - (void *)IDAPYTHON_RUNSTATEMENT); - - /* Add Load Python file menu item*/ - bool result = add_menu_item("File/Load file/IDC file...", "P~y~thon file...", - "Alt-9", SETMENU_APP, - (menu_item_callback_t *)IDAPython_Menu_Callback, - (void *)IDAPYTHON_RUNFILE); - if (!result) - add_menu_item("File/IDC command...", "P~y~thon file...", - "Alt-9", SETMENU_APP, - (menu_item_callback_t *)IDAPython_Menu_Callback, - (void *)IDAPYTHON_RUNFILE); - - /* Add View Python Scripts menu item*/ - result = add_menu_item("View/Open subviews/Show strings", "Python S~c~ripts", - "Alt-7", SETMENU_APP, - (menu_item_callback_t *)IDAPython_Menu_Callback, - (void *)IDAPYTHON_SCRIPTBOX); - if (!result) - add_menu_item("View/Open subviews/Problems", "Python S~c~ripts", - "Alt-7", SETMENU_APP, - (menu_item_callback_t *)IDAPython_Menu_Callback, - (void *)IDAPYTHON_SCRIPTBOX); - - menu_installed = true; -} - -enum script_run_when -{ - run_on_db_open = 0, // run script after opening database (default) - run_on_ui_ready = 1, // run script when UI is ready - run_on_init = 2, // run script immediately on plugin load (shortly after IDA starts) -}; - -static int g_run_when = -1; -static char g_run_script[QMAXPATH]; - -/* Parse plugin options */ -void parse_options() -{ - const char *options = get_plugin_options("IDAPython"); - if ( options == NULL ) - return; - const char *p = strchr(options, ';'); - if ( p == NULL ) - { - g_run_when = run_on_db_open; - qstrncpy(g_run_script, options, sizeof(g_run_script)); - } - else - { - g_run_when = atoi(options); - qstrncpy(g_run_script, p+1, sizeof(g_run_script)); - } -} - -/* we install the menu later because the text version crashes if -add_menu_item is called too early */ -static int idaapi menu_installer_cb(void *, int code, va_list) -{ - switch ( code ) - { - case ui_ready_to_run: - print_banner(); - install_python_menus(); - - if ( g_run_when == run_on_ui_ready ) - IDAPython_RunScript(g_run_script); - break; - - case ui_database_inited: - if ( g_run_when == run_on_db_open ) - IDAPython_RunScript(g_run_script); - break; - - default: - break; - } - return 0; -} - -/* Initialize the Python environment */ -bool IDAPython_Init(void) -{ - char tmp[MAXSTR+64]; - bool result = true; - - /* Already initialized? */ - if (initialized == 1) - return true; - - /* Check for the presence of essential files */ - initialized = 0; - - result &= CheckFile("idc.py"); - result &= CheckFile("init.py"); - result &= CheckFile("idaapi.py"); - result &= CheckFile("idautils.py"); - if (!result) - return false; - -#ifdef __LINUX__ - /* Export symbols from libpython to resolve imported module deps */ - qsnprintf(tmp, sizeof(tmp), "libpython%d.%d.so", - PY_MAJOR_VERSION, - PY_MINOR_VERSION); - if (!dlopen(tmp, RTLD_NOLOAD | RTLD_GLOBAL | RTLD_LAZY)) - { - warning("IDAPython: %s", dlerror()); - return false; - } -#endif - - /* Start the interpreter */ - Py_Initialize(); - if (!Py_IsInitialized()) - { - warning("IDAPython: Py_Initialize() failed"); - return false; - } - - /* Init the SWIG wrapper */ - init_idaapi(); - /* Set IDAPYTHON_VERSION in Python */ - qsnprintf(tmp, sizeof(tmp), "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)", \ - VER_MAJOR, - VER_MINOR, - VER_PATCH, - VER_STATUS, - VER_SERIAL); - begin_execution(); - PyRun_SimpleString(tmp); - end_execution(); - - /* Pull in the Python side of init */ - qmakepath(tmp, MAXSTR, idadir(PYTHON_DIR_NAME), "init.py", NULL); - if (!ExecFile(tmp)) - { - handle_python_error(tmp, sizeof(tmp)); - warning("IDAPython: error executing init.py:\n%s", tmp); - return false; - } - - /* Init pywraps (hand made/custom wrapper) */ - if (!init_pywraps()) - { - warning("IDAPython: init_pywraps() failed!"); - return false; - } - -#ifdef ENABLE_PYTHON_PROFILING - PyEval_SetTrace(tracefunc, NULL); -#endif - - /* Batch-mode operation: */ - /* A script specified on the command line is run */ - parse_options(); - if ( g_run_when == run_on_init ) - IDAPython_RunScript(g_run_script); - -#ifdef PLUGINFIX - hook_to_notification_point(HT_UI, menu_installer_cb, NULL); -#else - install_python_menus(); - print_banner(); -#endif - /* Register a RunPythonStatement() function for IDC */ - set_idc_func("RunPythonStatement", idc_runpythonstatement, idc_runpythonstatement_args); - -#if IDA_SDK_VERSION >= 540 - /* Enable the CLI by default */ - enable_python_cli(true); -#endif - -#if IDA_SDK_VERSION >= 560 - install_extlang(&extlang_python); -#endif - - initialized = 1; - - return true; -} - -/* Cleaning up Python */ -void IDAPython_Term(void) -{ -#ifdef PLUGINFIX - unhook_from_notification_point(HT_UI, menu_installer_cb, NULL); -#endif - /* Remove the menu items before termination */ - del_menu_item("File/Load file/Python file..."); - del_menu_item("File/Python file..."); - del_menu_item("File/Python command..."); - del_menu_item("View/Open subviews/Python Scripts"); - menu_installed = false; -#if IDA_SDK_VERSION >= 540 - /* Remove the CLI */ - enable_python_cli(false); -#endif - - /* Remove the extlang */ -#if IDA_SDK_VERSION >= 560 - remove_extlang(&extlang_python); -#else - register_extlang(NULL); -#endif - - /* De-init pywraps */ - deinit_pywraps(); - /* Shut the interpreter down */ - Py_Finalize(); - - initialized = 0; -} - -/* Plugin init routine */ -int idaapi init(void) -{ - if (IDAPython_Init()) - return PLUGIN_KEEP; - else - return PLUGIN_SKIP; -} - -/* Plugin term routine */ -void idaapi term(void) -{ - IDAPython_Term(); -} - -/* Plugin hotkey entry point */ -void idaapi run(int arg) -{ - try - { - switch (arg) - { - case 0: - IDAPython_RunScript(NULL); - break; - ;; - case 1: - IDAPython_RunStatement(); - break; - ;; - case 2: - IDAPython_ScriptBox(); - break; - ;; - case 3: - enable_extlang_python(true); - break; - ;; - case 4: - enable_extlang_python(false); - break; - ;; - default: - warning("IDAPython: unknown plugin argument %d", arg); - break; - ;; - } - } - catch(...) - { - warning("Exception in Python interpreter. Reloading..."); - IDAPython_Term(); - IDAPython_Init(); - } -} - -//-------------------------------------------------------------------------- -// PLUGIN DESCRIPTION BLOCK -//-------------------------------------------------------------------------- -char comment[] = "IDAPython"; -char help[] = "IDA Python Plugin\n"; -char wanted_name[] = "IDAPython"; -char wanted_hotkey[] = "Alt-9"; - -extern "C" -{ - plugin_t PLUGIN = { - IDP_INTERFACE_VERSION, -#ifdef PLUGINFIX - PLUGIN_FIX, // plugin flags -#else - 0, // plugin flags -#endif - init, // initialize - term, // terminate. this pointer may be NULL. - run, // invoke plugin - comment, // long comment about the plugin - // it could appear in the status line - // or as a hint - help, // multiline help about the plugin - wanted_name, // the preferred short name of the plugin - wanted_hotkey // the preferred hotkey to run the plugin - }; -} +//--------------------------------------------------------------------- +// 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. +//--------------------------------------------------------------------- +// python.cpp - Main plugin code +//--------------------------------------------------------------------- +#include + +//------------------------------------------------------------------------- +// This define fixes the redefinition of ssize_t +#ifdef HAVE_SSIZE_T +#define _SSIZE_T_DEFINED 1 +#endif + +#include +#include +#ifdef __LINUX__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pywraps.hpp" + +//------------------------------------------------------------------------- +// Defines and constants + +// Python-style version tuple comes from the makefile +// Only the serial and status is set here +#define VER_SERIAL 0 +#define VER_STATUS "final" +#define IDAPYTHON_RUNSTATEMENT 0 +#define IDAPYTHON_ENABLE_EXTLANG 3 +#define IDAPYTHON_DISABLE_EXTLANG 4 +#define PYTHON_DIR_NAME "python" +#define S_IDAPYTHON "IDAPython" +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[] = "Alt-8"; +static const char S_IDAPYTHON_DATA_NODE[] = "IDAPython_Data"; + +//------------------------------------------------------------------------- +// Types + +// +enum script_run_when +{ + run_on_db_open = 0, // run script after opening database (default) + run_on_ui_ready = 1, // run script when UI is ready + run_on_init = 2, // run script immediately on plugin load (shortly after IDA starts) +}; + +//------------------------------------------------------------------------- +// Global variables +static bool g_initialized = false; +static bool g_menu_installed = false; +static int g_run_when = -1; +static char g_run_script[QMAXPATH]; +static char g_idapython_dir[QMAXPATH]; + +//------------------------------------------------------------------------- +// Prototypes and forward declarations + +// Alias to SWIG_Init +extern "C" void init_idaapi(void); + +// Plugin run() callback +void idaapi run(int arg); + +//------------------------------------------------------------------------- +// This is a simple tracing code for debugging purposes. +// It might evolve into a tracing facility for user scripts. + +//#define ENABLE_PYTHON_PROFILING +#ifdef ENABLE_PYTHON_PROFILING +#include "compile.h" +#include "frameobject.h" + +int tracefunc(PyObject *obj, _frame *frame, int what, PyObject *arg) +{ + PyObject *str; + + /* Catch line change events. */ + /* Print the filename and line number */ + if ( what == PyTrace_LINE ) + { + str = PyObject_Str(frame->f_code->co_filename); + if ( str ) + { + msg("PROFILING: %s:%d\n", PyString_AsString(str), frame->f_lineno); + Py_DECREF(str); + } + } + return 0; +} +#endif + +//------------------------------------------------------------------------- +// Helper routines to make Python script execution breakable from IDA +static int ninsns = 0; // number of times trace function was called +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; +void end_execution(); +void begin_execution(); + +//------------------------------------------------------------------------ +// This callback is called on various interpreter events +static int break_check(PyObject *obj, _frame *frame, int what, PyObject *arg) +{ + if ( wasBreak() ) + { + /* 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 */ + ninsns = 0; + if ( script_timeout != 0 && (time(NULL) - start_time > script_timeout) ) /* Timeout elapsed? */ + { + box_displayed = true; + show_wait_box("Running Python script"); + } + } +#ifdef ENABLE_PYTHON_PROFILING + return tracefunc(obj, frame, what, arg); +#else + return 0; +#endif +} + +//------------------------------------------------------------------------ +// Prepare for Python execution +void begin_execution() +{ + if ( !g_ui_ready || script_timeout == 0 ) + return; + + ninsns = 0; + end_execution(); + box_displayed = false; + start_time = time(NULL); + PyEval_SetTrace(break_check, NULL); +} + +//------------------------------------------------------------------------ +// Called after Python execution finishes +void end_execution() +{ + if ( box_displayed ) + { + hide_wait_box(); + box_displayed = false; + } + +#ifdef ENABLE_PYTHON_PROFILING + PyEval_SetTrace(tracefunc, NULL); +#else + PyEval_SetTrace(NULL, NULL); +#endif +} + +//------------------------------------------------------------------------- +// Exported to the Python environment +int set_script_timeout(int timeout) +{ + if ( timeout == 0 ) + end_execution(); + + qswap(timeout, script_timeout); + + if ( script_timeout != 0 ) + begin_execution(); + return timeout; +} + +//------------------------------------------------------------------------- +// Converts IDC arguments to Python argument list or just one tuple +// If 'decref' is NULL then 'pargs' will contain one element which is the tuple +static bool convert_args( + const idc_value_t args[], + int nargs, + ppyobject_vec_t &pargs, + boolvec_t *decref, + char *errbuf, + size_t errbufsize) +{ + bool as_tupple = decref == NULL; + PyObject *py_tuple(NULL); + + pargs.qclear(); + + if ( as_tupple ) + { + py_tuple = PyTuple_New(nargs); + if ( py_tuple == NULL ) + { + qstrncpy(errbuf, "Failed to create a new tuple to store arguments!", errbufsize); + return false; + } + // Add the tuple + pargs.push_back(py_tuple); + } + else + { + decref->qclear(); + } + + for ( int i=0; ipush_back(cvt == CIP_OK); + } + } + return true; +} + +//------------------------------------------------------------------------- +// Frees arguments returned by convert_args() +static void free_args( + ppyobject_vec_t &pargs, + boolvec_t *decref = NULL) +{ + if ( decref == NULL ) + { + if ( !pargs.empty() ) + Py_XDECREF(pargs[0]); + } + else + { + // free argument objects + for ( int i=(int)pargs.size()-1; i>=0; i-- ) + { + if ( decref->at(i) ) + Py_DECREF(pargs[i]); + } + decref->clear(); + } + pargs.clear(); +} + +//------------------------------------------------------------------------ +// Return a formatted error or just print it to the console +static void handle_python_error(char *errbuf, size_t errbufsize) +{ + if ( errbufsize > 0 ) + errbuf[0] = '\0'; + + if ( !PyErr_Occurred() ) + return; + + PyObject *result; + PyObject *ptype, *pvalue, *ptraceback; + PyErr_Fetch(&ptype, &pvalue, &ptraceback); + result = PyObject_Str(pvalue); + if ( result != NULL ) + { + qsnprintf(errbuf, errbufsize, "ERROR: %s", PyString_AsString(result)); + PyErr_Clear(); + Py_XDECREF(ptype); + Py_XDECREF(pvalue); + Py_XDECREF(ptraceback); + Py_DECREF(result); + } + else + { + PyErr_Print(); + } +} + +//------------------------------------------------------------------------ +// Helper function to get globals for the __main__ module +// Note: The references are borrowed. No need to free them. +PyObject *GetMainGlobals() +{ + PyObject *module = PyImport_AddModule(S_MAIN); + if ( module == NULL ) + return NULL; + return PyModule_GetDict(module); +} + +//------------------------------------------------------------------------ +// Simple Python statement runner function for IDC +static const char idc_runpythonstatement_args[] = { VT_STR2, 0 }; +static error_t idaapi idc_runpythonstatement(idc_value_t *argv, idc_value_t *res) +{ + PyObject *globals = GetMainGlobals(); + if ( globals == NULL ) + { + res->set_string("internal error"); + } + else + { + PyErr_Clear(); + + begin_execution(); + PyObject *result = PyRun_String(argv[0].c_str(), Py_file_input, globals, globals ); + Py_XDECREF(result); + end_execution(); + + if ( result == NULL || PyErr_Occurred() ) + { + char errbuf[MAXSTR]; + handle_python_error(errbuf, sizeof(errbuf)); + if ( errbuf[0] == '\0' ) + res->set_string("internal error"); + else + res->set_string(errbuf); + } + else + { + // success + res->set_long(0); + } + } + return eOk; +} + +//------------------------------------------------------------------------- +// Check for the presence of a file in IDADIR/python and complain on error +bool CheckScriptFiles() +{ + static const char *const script_files[] = + { + S_IDC_MODNAME ".py", + "init.py", + "idaapi.py", + "idautils.py" + }; + for ( size_t i=0; i 0 ) + errbuf[0] = '\0'; + + if ( py_result == NULL ) + { + handle_python_error(errbuf, errbufsize); + return false; + } + + int cvt = CIP_OK; + if ( idc_result != NULL ) + { + idc_result->clear(); + cvt = pyvar_to_idcvar(py_result, idc_result); + if ( cvt < CIP_OK ) + qsnprintf(errbuf, errbufsize, "ERROR: bad return value"); + } + + if ( cvt != CIP_OK_NODECREF ) + Py_XDECREF(py_result); + + return cvt >= CIP_OK; +} + +//------------------------------------------------------------------------- +// Compile callback for Python external language evaluator +bool idaapi IDAPython_extlang_compile( + const char *name, + ea_t /*current_ea*/, + const char *expr, + char *errbuf, + size_t errbufsize) +{ + PyObject *globals = GetMainGlobals(); + QASSERT(globals != NULL); + + PyCodeObject *code = (PyCodeObject *)Py_CompileString(expr, "", Py_eval_input); + if ( code == NULL ) + { + handle_python_error(errbuf, errbufsize); + return false; + } + + // set the desired function name + Py_XDECREF(code->co_name); + code->co_name = PyString_FromString(name); + + // create a function out of code + PyObject *func = PyFunction_New((PyObject *)code, globals); + + if ( func == NULL ) + { +ERR: + handle_python_error(errbuf, errbufsize); + Py_XDECREF(code); + return false; + } + + int err = PyDict_SetItemString(globals, name, func); + Py_XDECREF(func); + if ( err ) + goto ERR; + + return true; +} + +//------------------------------------------------------------------------- +// Run callback for Python external language evaluator +bool idaapi IDAPython_extlang_run( + const char *name, + int nargs, + const idc_value_t args[], + idc_value_t *result, + char *errbuf, + size_t errbufsize) +{ + // Try to extract module name (if any) from the funcname + char modname[MAXSTR] = {0}; + char funcname[MAXSTR] = {0}; + bool imported_module = parse_py_modname(name, modname, funcname, MAXSTR); + + ppyobject_vec_t pargs; + boolvec_t decref; + bool ok = true; + PyObject *module = NULL; + do + { + // Convert arguments to python + ok = convert_args(args, nargs, pargs, &decref, errbuf, errbufsize); + if ( !ok ) + break; + + if ( imported_module ) + { + module = PyImport_ImportModule(modname); + } + else + { + module = PyImport_AddModule(S_MAIN); + QASSERT(module != NULL); + } + + PyObject *globals = PyModule_GetDict(module); + QASSERT(globals != NULL); + + PyObject *func = PyDict_GetItemString(globals, funcname); + if ( func == NULL ) + { + qsnprintf(errbuf, errbufsize, "undefined function %s", name); + ok = false; + break; + } + PyCodeObject *code = (PyCodeObject *) PyFunction_GetCode(func); + PyObject *pres = PyEval_EvalCodeEx( + code, + globals, NULL, &pargs[0], nargs, + NULL, 0, NULL, 0, NULL); + ok = return_python_result(result, pres, errbuf, errbufsize); + } while ( false ); + + free_args(pargs, &decref); + + if ( imported_module ) + Py_XDECREF(module); + return ok; +} + +//------------------------------------------------------------------------- +// Compile callback for Python external language evaluator +bool idaapi IDAPython_extlang_compile_file( + const char *filename, + char *errbuf, + size_t errbufsize) +{ + begin_execution(); + bool ok = IDAPython_ExecFile(filename, errbuf, errbufsize); + end_execution(); + return ok; +} + +//------------------------------------------------------------------------- +// Create an object instance +bool idaapi IDAPython_extlang_create_object( + const char *name, // in: object class name + int nargs, // in: number of input arguments + const idc_value_t args[], // in: input arguments + idc_value_t *result, // out: created object or exception + char *errbuf, // out: error message if evaluation fails + size_t errbufsize) // in: size of the error buffer +{ + PyObject *py_mod(NULL), *py_cls(NULL); + ppyobject_vec_t pargs; + + bool ok = false; + do + { + // Parse the object name (to get the module and class name) + char modname[MAXSTR] = {0}; + char clsname[MAXSTR] = {0}; + parse_py_modname(name, modname, clsname, MAXSTR); + + // Get a reference to the module + py_mod = PyImport_TryImportModule(modname); + if ( py_mod == NULL ) + { + qsnprintf(errbuf, errbufsize, "Could not import module '%s'!", modname); + break; + } + + // Get the class reference + py_cls = PyObject_TryGetAttrString(py_mod, clsname); + if ( py_cls == NULL ) + { + qsnprintf(errbuf, errbufsize, "Could not find class type '%s'!", clsname); + break; + } + + // Error during conversion? + ok = convert_args(args, nargs, pargs, NULL, errbuf, errbufsize); + if ( !ok ) + break; + ok = false; + + // Call the constructor + PyObject *py_res = PyObject_CallObject(py_cls, pargs.empty() ? NULL : pargs[0]); + ok = return_python_result(result, py_res, errbuf, errbufsize); + } while ( false ); + + Py_XDECREF(py_mod); + Py_XDECREF(py_cls); + + // Free the arguments tuple + free_args(pargs); + return ok; +} + +//------------------------------------------------------------------------- +// Returns the attribute value of a given object from the global scope +bool idaapi IDAPython_extlang_get_attr( + const idc_value_t *obj, // in: object (may be NULL) + const char *attr, // in: attribute name + idc_value_t *result) +{ + PyObject *py_mod(NULL), *py_obj(NULL); + bool is_opaque_obj = false; + int cvt = CIP_FAILED; + do + { + // Get a reference to the module + py_mod = PyImport_TryImportModule(S_MAIN); + if ( py_mod == NULL ) + break; + + // Object specified: + // - (1) string contain attribute name in the main module + // - (2) opaque object (we use it as is) + if ( obj != NULL ) + { + // (1) Get attribute from main module + if ( obj->vtype == VT_STR2 ) + py_obj = PyObject_TryGetAttrString(py_mod, obj->c_str()); + // (2) see if opaque object + else + { + // Convert object (expecting opaque object) + cvt = idcvar_to_pyvar(*obj, &py_obj); + // Only opaque objects are accepted + if ( cvt != CIP_OK_NODECREF ) + { + Py_XDECREF(py_obj); + py_obj = NULL; + cvt = CIP_FAILED; + break; + } + is_opaque_obj = true; + } + // Get the attribute reference + if ( py_obj == NULL ) + break; + } + // No object specified: + else + { + // then work with main module + py_obj = py_mod; + } + PyObject *py_attr = PyObject_TryGetAttrString(py_obj, attr); + // No attribute? + if ( py_attr == NULL ) + { + cvt = CIP_FAILED; + break; + } + // Don't store result + if ( result == NULL ) + { + cvt = CIP_OK; + // Decrement attribute (because of GetAttrString) + Py_DECREF(py_attr); + } + else + { + cvt = pyvar_to_idcvar(py_attr, result); + // Conversion succeeded and opaque object was passed: + // Since the object will be passed to IDC, it is likely that IDC value will be + // destroyed and also destroying the opaque object with it. That is an undesired effect. + // We increment the reference of the object so that even if the IDC value dies + // the opaque object remains. So by not decrement reference after GetAttrString() call + // we are indirectly increasing the reference. If it was not opaque then we decrement the reference. + if ( cvt >= CIP_OK && cvt != CIP_OK_NODECREF ) + { + // Decrement the reference (that was incremented by GetAttrString()) + Py_DECREF(py_attr); + } + } + } while ( false ); + + // Free main module reference + Py_XDECREF(py_mod); + + // Wasn't working with main module? + if ( obj != NULL && !is_opaque_obj ) + Py_XDECREF(py_obj); + + return cvt >= CIP_OK; +} + +//------------------------------------------------------------------------- +// Returns the attribute value of a given object from the global scope +bool idaapi IDAPython_extlang_set_attr( + idc_value_t *obj, // in: object name (may be NULL) + const char *attr, // in: attribute name + idc_value_t *value) +{ + PyObject *py_mod(NULL), *py_obj(NULL); + bool ok = false, is_opaque_obj = false; + do + { + // Get a reference to the module + py_mod = PyImport_TryImportModule(S_MAIN); + if ( py_mod == NULL ) + break; + + if ( obj != NULL ) + { + // Get the attribute reference (from just a name) + if ( obj->vtype == VT_STR2 ) + py_obj = PyObject_TryGetAttrString(py_mod, obj->c_str()); + else + { + int cvt = idcvar_to_pyvar(*obj, &py_obj); + // Only opaque objects are accepted + if ( cvt != CIP_OK_NODECREF ) + { + Py_XDECREF(py_obj); + py_obj = NULL; + } + else + is_opaque_obj = true; + } + // No object to set_attr on? + if ( py_obj == NULL ) + break; + } + else + { + // set_attr on the main module + py_obj = py_mod; + } + // Convert the value + PyObject *py_var(NULL); + int cvt = idcvar_to_pyvar(*value, &py_var); + if ( cvt >= CIP_OK ) + { + ok = PyObject_SetAttrString(py_obj, attr, py_var) != -1; + if ( cvt != CIP_OK_NODECREF ) + Py_XDECREF(py_var); + } + } while ( false ); + + Py_XDECREF(py_mod); + + if ( obj != NULL && !is_opaque_obj ) + Py_XDECREF(py_obj); + + return ok; +} + +//------------------------------------------------------------------------- +// Calculator callback for Python external language evaluator +bool idaapi IDAPython_extlang_calcexpr( + ea_t /*current_ea*/, + const char *expr, + idc_value_t *rv, + char *errbuf, + size_t errbufsize) +{ + PyObject *globals = GetMainGlobals(); + if ( globals == NULL ) + return false; + + begin_execution(); + PyObject *result = PyRun_String(expr, Py_eval_input, globals, globals); + end_execution(); + + return return_python_result(rv, result, errbuf, errbufsize); +} + +//------------------------------------------------------------------------- +bool idaapi IDAPython_extlang_call_method( + const idc_value_t *idc_obj, + const char *method_name, + int nargs, + const idc_value_t args[], + idc_value_t *result, + char *errbuf, + size_t errbufsize) +{ + // Check for unsupported usage of call_method. + // Mainly a method call requires an object and a method. + if ( (idc_obj == NULL && method_name == NULL) || (idc_obj != NULL && method_name == NULL) ) + { + qstrncpy(errbuf, "call_method does not support this operation", errbufsize); + return false; + } + // Behave like run() + else if ( idc_obj == NULL && method_name != NULL ) + return IDAPython_extlang_run(method_name, nargs, args, result, errbuf, errbufsize); + + PyObject *py_obj(NULL), *py_method(NULL); + // Holds conversion status of input object + int obj_cvt; + ppyobject_vec_t pargs; + bool ok = false; + do + { + // Convert input object + obj_cvt = idcvar_to_pyvar(*idc_obj, &py_obj); + if ( obj_cvt < CIP_OK ) + { + qstrncpy(errbuf, "Failed to convert input object to Python value", errbufsize); + break; + } + + py_method = PyObject_TryGetAttrString(py_obj, method_name); + if ( py_method == NULL || !PyCallable_Check(py_method) ) + { + qsnprintf(errbuf, errbufsize, "The input object does not have a callable method called '%s'", method_name); + break; + } + + // Convert arguments to python objects + ok = convert_args(args, nargs, pargs, NULL, errbuf, errbufsize); + if ( !ok ) + break; + + PyObject *py_res = PyObject_CallObject(py_method, pargs.empty() ? NULL : pargs[0]); + ok = return_python_result(result, py_res, errbuf, errbufsize); + } while ( false ); + + // Free converted args + free_args(pargs); + + // Release reference of object if needed + if ( obj_cvt != CIP_OK_NODECREF ) + Py_XDECREF(py_obj); + + Py_XDECREF(py_method); + return ok; +} + +//------------------------------------------------------------------------- +extlang_t extlang_python = +{ + sizeof(extlang_t), + 0, + "Python", + IDAPython_extlang_compile, + IDAPython_extlang_run, + IDAPython_extlang_calcexpr, + IDAPython_extlang_compile_file, + "py", + IDAPython_extlang_create_object, + IDAPython_extlang_get_attr, + IDAPython_extlang_set_attr, + IDAPython_extlang_call_method +}; + +void enable_extlang_python(bool enable) +{ + if ( enable ) + select_extlang(&extlang_python); + else + select_extlang(NULL); +} + +//------------------------------------------------------------------------- +// Execute a line in the Python CLI +bool idaapi IDAPython_cli_execute_line(const char *line) +{ + const char *first_line = strrchr(line, '\n'); + if ( first_line == NULL ) + first_line = line; + else + first_line += 1; + + // skip empty lines + if ( first_line[0] != '\0' ) + { + // take a copy of the line so we r-trim it + char *tline = qstrdup(first_line); + trim(tline); + + // line ends with ":" or begins with a space character? + bool more = tline[qstrlen(tline)-1] == ':' || isspace(first_line[0]); + qfree(tline); + + if ( more ) + return false; + } + + begin_execution(); + PyRun_SimpleString(line); + end_execution(); + + return true; +} + +//------------------------------------------------------------------------- +static const cli_t cli_python = +{ + sizeof(cli_t), + 0, + "Python", + "Python - IDAPython plugin", + "Enter any Python expression", + IDAPython_cli_execute_line, + NULL, + NULL +}; + +//------------------------------------------------------------------------- +// Control the Python CLI status +void enable_python_cli(bool enable) +{ + if ( enable ) + install_command_interpreter(&cli_python); + else + remove_command_interpreter(&cli_python); +} + +//------------------------------------------------------------------------- +// Prints the IDAPython copyright banner +void py_print_banner() +{ + PyRun_SimpleString("print_banner()"); +} + +//------------------------------------------------------------------------- +// Install python menu items +static void install_python_menus() +{ + if ( g_menu_installed ) + return; + + // 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, + IDAPython_Menu_Callback, + (void *)IDAPYTHON_RUNSTATEMENT); + + g_menu_installed = true; +} + +//------------------------------------------------------------------------ +// Parse plugin options +void parse_plugin_options() +{ + // Get options from IDA + const char *options = get_plugin_options(S_IDAPYTHON); + + // No options? + if ( options == NULL ) + return; + + // User specified 'when' parameter? + const char *p = strchr(options, ';'); + if ( p == NULL ) + { + g_run_when = run_on_db_open; + p = options; + } + else + { + g_run_when = atoi(options); + ++p; + } + qstrncpy(g_run_script, p, sizeof(g_run_script)); +} + +//------------------------------------------------------------------------ +// Converts the global IDC variable "ARGV" into a Python variable. +// The arguments will then be accessible via 'idc' module / 'ARGV' variable. +void convert_idc_args() +{ + PyObject *py_args = PyList_New(0); + + idc_value_t *idc_args = find_idc_gvar(S_IDC_ARGS_VARNAME); + if ( idc_args != NULL ) + { + idc_value_t attr; + char attr_name[20] = {"0"}; + for ( int i=1; VarGetAttr(idc_args, attr_name, &attr) == eOk; i++ ) + { + PyList_Insert(py_args, i, PyString_FromString(attr.c_str())); + qsnprintf(attr_name, sizeof(attr_name), "%d", i); + } + } + + // Get reference to the IDC module (it is imported by init.py) + PyObject *py_mod = PyImport_TryImportModule(S_IDC_MODNAME); + if ( py_mod != NULL ) + PyObject_SetAttrString(py_mod, S_IDC_ARGS_VARNAME, py_args); + + Py_DECREF(py_args); + Py_XDECREF(py_mod); +} + +//------------------------------------------------------------------------ +// We install the menu later because the text version crashes if +// add_menu_item is called too early +static int idaapi menu_installer_cb(void *, int code, va_list) +{ + switch ( code ) + { + case ui_ready_to_run: + g_ui_ready = true; + py_print_banner(); + install_python_menus(); + + if ( g_run_when == run_on_ui_ready ) + RunScript(g_run_script); + break; + + case ui_database_inited: + { + convert_idc_args(); + if ( g_run_when == run_on_db_open ) + RunScript(g_run_script); + break; + } + default: + break; + } + return 0; +} + +//------------------------------------------------------------------------- +// Initialize the Python environment +bool IDAPython_Init(void) +{ + // Already initialized? + if ( g_initialized ) + return true; + + // Form the absolute path to IDA\python folder + qstrncpy(g_idapython_dir, idadir(PYTHON_DIR_NAME), sizeof(g_idapython_dir)); + + // Check for the presence of essential files + if ( !CheckScriptFiles() ) + return false; + + char tmp[MAXSTR+64]; +#ifdef __LINUX__ + // Export symbols from libpython to resolve imported module deps + qsnprintf(tmp, sizeof(tmp), "libpython%d.%d.so", + PY_MAJOR_VERSION, + PY_MINOR_VERSION); + if ( !dlopen(tmp, RTLD_NOLOAD | RTLD_GLOBAL | RTLD_LAZY) ) + { + warning("IDAPython: %s", dlerror()); + return false; + } +#endif + + // Start the interpreter + Py_Initialize(); + if ( !Py_IsInitialized() ) + { + warning("IDAPython: Py_Initialize() failed"); + return false; + } + + // Init the SWIG wrapper + init_idaapi(); + + // Set IDAPYTHON_VERSION in Python + qsnprintf(tmp, sizeof(tmp), "IDAPYTHON_VERSION=(%d, %d, %d, '%s', %d)", \ + VER_MAJOR, + VER_MINOR, + VER_PATCH, + VER_STATUS, + VER_SERIAL); + PyRun_SimpleString(tmp); + + // Execute init.py (for Python side initialization) + qmakepath(tmp, MAXSTR, g_idapython_dir, "init.py", NULL); + if ( !PyRunFile(tmp) ) + { + handle_python_error(tmp, sizeof(tmp)); + warning("IDAPython: error executing init.py:\n%s", tmp); + return false; + } + + // Init pywraps and notify_when + if ( !init_pywraps() || !pywraps_nw_init() ) + { + warning("IDAPython: init_pywraps() failed!"); + return false; + } + +#ifdef ENABLE_PYTHON_PROFILING + PyEval_SetTrace(tracefunc, NULL); +#endif + + // Batch-mode operation: + parse_plugin_options(); + + // Register a RunPythonStatement() function for IDC + set_idc_func(S_IDC_RUNPYTHON_STATEMENT, idc_runpythonstatement, idc_runpythonstatement_args); + + // A script specified on the command line is run + if ( g_run_when == run_on_init ) + RunScript(g_run_script); + +#ifdef PLUGINFIX + hook_to_notification_point(HT_UI, menu_installer_cb, NULL); +#else + install_python_menus(); + py_print_banner(); +#endif + + // Enable the CLI by default + enable_python_cli(true); + + // Install extlang + install_extlang(&extlang_python); + + g_initialized = true; + pywraps_nw_notify(NW_INITIDA_SLOT); + return true; +} + +//------------------------------------------------------------------------- +// Cleaning up Python +void IDAPython_Term(void) +{ +#ifdef PLUGINFIX + unhook_from_notification_point(HT_UI, menu_installer_cb, NULL); +#endif + /* Remove the menu items before termination */ + 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(); + + // De-init pywraps + deinit_pywraps(); + + // Uninstall IDC function + set_idc_func(S_IDC_RUNPYTHON_STATEMENT, NULL, NULL); + + // Shut the interpreter down + Py_Finalize(); + + g_initialized = false; +} + +//------------------------------------------------------------------------- +// Plugin init routine +int idaapi init(void) +{ + if ( IDAPython_Init() ) + return PLUGIN_KEEP; + else + return PLUGIN_SKIP; +} + +//------------------------------------------------------------------------- +// Plugin term routine +void idaapi term(void) +{ + IDAPython_Term(); +} + +//------------------------------------------------------------------------- +// Plugin hotkey entry point +void idaapi run(int arg) +{ + try + { + switch ( arg ) + { + case IDAPYTHON_RUNSTATEMENT: + IDAPython_RunStatement(); + break; + case IDAPYTHON_ENABLE_EXTLANG: + enable_extlang_python(true); + break; + case IDAPYTHON_DISABLE_EXTLANG: + enable_extlang_python(false); + break; + default: + warning("IDAPython: unknown plugin argument %d", arg); + break; + } + } + catch(...) + { + warning("Exception in Python interpreter. Reloading..."); + IDAPython_Term(); + IDAPython_Init(); + } +} + +//------------------------------------------------------------------------- +// PLUGIN DESCRIPTION BLOCK +//------------------------------------------------------------------------- +plugin_t PLUGIN = +{ + IDP_INTERFACE_VERSION, +#ifdef PLUGINFIX + PLUGIN_FIX, // plugin flags +#else + 0, // plugin flags +#endif + init, // initialize + term, // terminate. this pointer may be NULL. + run, // invoke plugin + S_IDAPYTHON, // long comment about the plugin + // it could appear in the status line + // or as a hint + // multiline help about the plugin + "IDA Python Plugin\n", + // the preferred short name of the plugin + S_IDAPYTHON, + // the preferred hotkey to run the plugin + S_HOTKEY_RUNSTATEMENT +}; diff --git a/python/idautils.py b/python/idautils.py index 91eea5a..8c49b12 100644 --- a/python/idautils.py +++ b/python/idautils.py @@ -1,568 +1,547 @@ -#--------------------------------------------------------------------- -# 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 - -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(), 1): - 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(), 1): - 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 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 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 an insn_t like structure - """ - inslen = idaapi.decode_insn(ea) - if inslen == 0: - return None - insn = idaapi.get_current_instruction() - if not insn: - return None - - class _insn(object): - def __getitem__(self, index): - if index > len(self.Operands): - raise StopIteration - return self.Operands[index] - - class _op(_reg_dtyp_t): - def __init__(self, op): - _copy_obj(op, self) - _reg_dtyp_t.__init__(self, op.reg, op.dtyp) - def is_reg(self, r): - """Checks if the operand is the given processor register""" - return self.type == idaapi.o_reg and self == r - def has_reg(self, r): - """Checks if the operand accesses the given processor register""" - return self.reg == r.reg - r = _copy_obj(insn, _insn()) - r.Operands = [] # to hold the operands - for n in xrange(0, idaapi.UA_MAXOP): - t = idaapi.get_instruction_operand(insn, n) - if t.type == idaapi.o_void: - break - r.Operands.append(_op(t)) - return r - - -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. - The attributes are: - ea - string ea - type - string type (ASCSTR_xxxxx) - str() - returns the actual string - """ - def __init__(self, si): - self.ea = si.ea - self.type = si.type - self.length = si.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): - if default_setup: - self.setup() - self._si = idaapi.string_info_t() - self.size = 0 - - def refresh(self, ea1=None, ea2=None): - """Refreshes the strings list""" - if not ea1: ea1 = idaapi.cvar.inf.minEA - if not ea2: 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 not ea1: ea1 = idaapi.cvar.inf.minEA - if not ea2: 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 string items""" - if index >= self.size: - raise StopIteration - if idaapi.get_strlist_item(index, self._si): - return Strings.StringItem(self._si) - return None - -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): - """ - 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: - dest = new.classobj(dest, (), {}) - for x in dir(src): - if x.startswith("__") and x.endswith("__"): - continue - t = getattr(src, x) - 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) - -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 + +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(), 1): + 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(), 1): + 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 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 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. + The attributes are: + ea - string ea + type - string type (ASCSTR_xxxxx) + str() - returns the actual string + """ + def __init__(self, si): + self.ea = si.ea + self.type = si.type + self.length = si.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): + if default_setup: + self.setup() + self._si = idaapi.string_info_t() + self.size = 0 + + def refresh(self, ea1=None, ea2=None): + """Refreshes the strings list""" + if not ea1: ea1 = idaapi.cvar.inf.minEA + if not ea2: 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 not ea1: ea1 = idaapi.cvar.inf.minEA + if not ea2: 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 string items""" + if index >= self.size: + raise StopIteration + if idaapi.get_strlist_item(index, self._si): + return Strings.StringItem(self._si) + return None + +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) + +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 7b0e423..a694415 100644 --- a/python/idc.py +++ b/python/idc.py @@ -1,7530 +1,7551 @@ -#!/usr/bin/env python -#--------------------------------------------------------------------- -# IDAPython - Python plugin for Interactive Disassembler Pro -# -# Original IDC.IDC: -# Copyright (c) 1990-2010 Ilfak Guilfanov -# -# Python conversion: -# Copyright (c) 2004-2010 Gergely Erdelyi -# -# All rights reserved. -# -# For detailed copyright information see the file COPYING in -# the root of the distribution archive. -#--------------------------------------------------------------------- -# idc.py - IDC compatibility module -#--------------------------------------------------------------------- -""" -IDC compatibility module - -This file contains IDA built-in function declarations and internal bit -definitions. Each byte of the program has 32-bit flags (low 8 bits keep -the byte value). These 32 bits are used in GetFlags/SetFlags functions. -You may freely examine these bits using GetFlags() but the use of the -SetFlags() function is strongly discouraged. - -This file is subject to change without any notice. -Future versions of IDA may use other definitions. -""" -try: - import idaapi -except ImportError: - print "Could not import idaapi. Running in 'pydoc mode'." - -import os -import re -import struct -import time -import types - -__EA64__ = idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL -WORDMASK = __EA64__ and 0xFFFFFFFFFFFFFFFF or 0xFFFFFFFF - -class DeprecatedIDCError(Exception): - """ - Exception for deprecated function calls - """ - pass - - -def _IDC_GetAttr(obj, attrmap, attroffs): - """ - Internal function to generically get object attributes - Do not use unless you know what you are doing - """ - if attroffs in attrmap and hasattr(obj, attrmap[attroffs][1]): - return getattr(obj, attrmap[attroffs][1]) - else: - errormsg = "attribute with offset %d not found, check the offset and report the problem" % attroffs - raise KeyError, errormsg - - -def _IDC_SetAttr(obj, attrmap, attroffs, value): - """ - Internal function to generically set object attributes - Do not use unless you know what you are doing - """ - # check for read-only atributes - if attroffs in attrmap: - if attrmap[attroffs][0]: - raise KeyError, "attribute with offset %d is read-only" % attroffs - elif hasattr(obj, attrmap[attroffs][1]): - return setattr(obj, attrmap[attroffs][1], value) - errormsg = "attribute with offset %d not found, check the offset and report the problem" % attroffs - raise KeyError, errormsg - - -BADADDR = idaapi.BADADDR # Not allowed address value -BADSEL = idaapi.BADSEL # Not allowed selector value/number -MAXADDR = idaapi.MAXADDR & WORDMASK - -# -# Flag bit definitions (for GetFlags()) -# -MS_VAL = idaapi.MS_VAL # Mask for byte value -FF_IVL = idaapi.FF_IVL # Byte has value ? - -# Do flags contain byte value? (i.e. has the byte a value?) -# if not, the byte is uninitialized. - -def hasValue(F): return ((F & FF_IVL) != 0) # any defined value? - -# Get byte value from flags -# Get value of byte provided that the byte is initialized. -# This macro works ok only for 8-bit byte machines. - -def byteValue(F): return (F & MS_VAL) # quick replacement for Byte() - -# Is the byte initialized? - -def isLoaded(ea): return hasValue(GetFlags(ea)) # any defined value? - -MS_CLS = idaapi.MS_CLS # Mask for typing -FF_CODE = idaapi.FF_CODE # Code ? -FF_DATA = idaapi.FF_DATA # Data ? -FF_TAIL = idaapi.FF_TAIL # Tail ? -FF_UNK = idaapi.FF_UNK # Unknown ? - -def isCode(F): return ((F & MS_CLS) == FF_CODE) # is code byte? -def isData(F): return ((F & MS_CLS) == FF_DATA) # is data byte? -def isTail(F): return ((F & MS_CLS) == FF_TAIL) # is tail byte? -def isUnknown(F): return ((F & MS_CLS) == FF_UNK) # is unexplored byte? -def isHead(F): return ((F & FF_DATA) != 0) # is start of code/data? - -# -# Common bits -# -MS_COMM = idaapi.MS_COMM # Mask of common bits -FF_COMM = idaapi.FF_COMM # Has comment? -FF_REF = idaapi.FF_REF # has references? -FF_LINE = idaapi.FF_LINE # Has next or prev cmt lines ? -FF_NAME = idaapi.FF_NAME # Has user-defined name ? -FF_LABL = idaapi.FF_LABL # Has dummy name? -FF_FLOW = idaapi.FF_FLOW # Exec flow from prev instruction? -FF_VAR = idaapi.FF_VAR # Is byte variable ? -FF_ANYNAME = FF_LABL | FF_NAME - -def isFlow(F): return ((F & FF_FLOW) != 0) -def isVar(F): return ((F & FF_VAR ) != 0) -def isExtra(F): return ((F & FF_LINE) != 0) -def isRef(F): return ((F & FF_REF) != 0) -def hasName(F): return ((F & FF_NAME) != 0) -def hasUserName(F): return ((F & FF_ANYNAME) == FF_NAME) - -MS_0TYPE = idaapi.MS_0TYPE # Mask for 1st arg typing -FF_0VOID = idaapi.FF_0VOID # Void (unknown)? -FF_0NUMH = idaapi.FF_0NUMH # Hexadecimal number? -FF_0NUMD = idaapi.FF_0NUMD # Decimal number? -FF_0CHAR = idaapi.FF_0CHAR # Char ('x')? -FF_0SEG = idaapi.FF_0SEG # Segment? -FF_0OFF = idaapi.FF_0OFF # Offset? -FF_0NUMB = idaapi.FF_0NUMB # Binary number? -FF_0NUMO = idaapi.FF_0NUMO # Octal number? -FF_0ENUM = idaapi.FF_0ENUM # Enumeration? -FF_0FOP = idaapi.FF_0FOP # Forced operand? -FF_0STRO = idaapi.FF_0STRO # Struct offset? -FF_0STK = idaapi.FF_0STK # Stack variable? - -MS_1TYPE = idaapi.MS_1TYPE # Mask for 2nd arg typing -FF_1VOID = idaapi.FF_1VOID # Void (unknown)? -FF_1NUMH = idaapi.FF_1NUMH # Hexadecimal number? -FF_1NUMD = idaapi.FF_1NUMD # Decimal number? -FF_1CHAR = idaapi.FF_1CHAR # Char ('x')? -FF_1SEG = idaapi.FF_1SEG # Segment? -FF_1OFF = idaapi.FF_1OFF # Offset? -FF_1NUMB = idaapi.FF_1NUMB # Binary number? -FF_1NUMO = idaapi.FF_1NUMO # Octal number? -FF_1ENUM = idaapi.FF_1ENUM # Enumeration? -FF_1FOP = idaapi.FF_1FOP # Forced operand? -FF_1STRO = idaapi.FF_1STRO # Struct offset? -FF_1STK = idaapi.FF_1STK # Stack variable? - -# The following macros answer questions like -# 'is the 1st (or 2nd) operand of instruction or data of the given type'? -# Please note that data items use only the 1st operand type (is...0) - -def isDefArg0(F): return ((F & MS_0TYPE) != FF_0VOID) -def isDefArg1(F): return ((F & MS_1TYPE) != FF_1VOID) -def isDec0(F): return ((F & MS_0TYPE) == FF_0NUMD) -def isDec1(F): return ((F & MS_1TYPE) == FF_1NUMD) -def isHex0(F): return ((F & MS_0TYPE) == FF_0NUMH) -def isHex1(F): return ((F & MS_1TYPE) == FF_1NUMH) -def isOct0(F): return ((F & MS_0TYPE) == FF_0NUMO) -def isOct1(F): return ((F & MS_1TYPE) == FF_1NUMO) -def isBin0(F): return ((F & MS_0TYPE) == FF_0NUMB) -def isBin1(F): return ((F & MS_1TYPE) == FF_1NUMB) -def isOff0(F): return ((F & MS_0TYPE) == FF_0OFF) -def isOff1(F): return ((F & MS_1TYPE) == FF_1OFF) -def isChar0(F): return ((F & MS_0TYPE) == FF_0CHAR) -def isChar1(F): return ((F & MS_1TYPE) == FF_1CHAR) -def isSeg0(F): return ((F & MS_0TYPE) == FF_0SEG) -def isSeg1(F): return ((F & MS_1TYPE) == FF_1SEG) -def isEnum0(F): return ((F & MS_0TYPE) == FF_0ENUM) -def isEnum1(F): return ((F & MS_1TYPE) == FF_1ENUM) -def isFop0(F): return ((F & MS_0TYPE) == FF_0FOP) -def isFop1(F): return ((F & MS_1TYPE) == FF_1FOP) -def isStroff0(F): return ((F & MS_0TYPE) == FF_0STRO) -def isStroff1(F): return ((F & MS_1TYPE) == FF_1STRO) -def isStkvar0(F): return ((F & MS_0TYPE) == FF_0STK) -def isStkvar1(F): return ((F & MS_1TYPE) == FF_1STK) - -# -# Bits for DATA bytes -# -DT_TYPE = idaapi.DT_TYPE & 0xFFFFFFFF # Mask for DATA typing - -FF_BYTE = idaapi.FF_BYTE & 0xFFFFFFFF # byte -FF_WORD = idaapi.FF_WORD & 0xFFFFFFFF # word -FF_DWRD = idaapi.FF_DWRD & 0xFFFFFFFF # dword -FF_QWRD = idaapi.FF_QWRD & 0xFFFFFFFF # qword -FF_TBYT = idaapi.FF_TBYT & 0xFFFFFFFF # tbyte -FF_ASCI = idaapi.FF_ASCI & 0xFFFFFFFF # ASCII ? -FF_STRU = idaapi.FF_STRU & 0xFFFFFFFF # Struct ? -FF_OWRD = idaapi.FF_OWRD & 0xFFFFFFFF # octaword (16 bytes) -FF_FLOAT = idaapi.FF_FLOAT & 0xFFFFFFFF # float -FF_DOUBLE = idaapi.FF_DOUBLE & 0xFFFFFFFF # double -FF_PACKREAL = idaapi.FF_PACKREAL & 0xFFFFFFFF # packed decimal real -FF_ALIGN = idaapi.FF_ALIGN & 0xFFFFFFFF # alignment directive - -def isByte(F): return (isData(F) and (F & DT_TYPE) == FF_BYTE) -def isWord(F): return (isData(F) and (F & DT_TYPE) == FF_WORD) -def isDwrd(F): return (isData(F) and (F & DT_TYPE) == FF_DWRD) -def isQwrd(F): return (isData(F) and (F & DT_TYPE) == FF_QWRD) -def isOwrd(F): return (isData(F) and (F & DT_TYPE) == FF_OWRD) -def isTbyt(F): return (isData(F) and (F & DT_TYPE) == FF_TBYT) -def isFloat(F): return (isData(F) and (F & DT_TYPE) == FF_FLOAT) -def isDouble(F): return (isData(F) and (F & DT_TYPE) == FF_DOUBLE) -def isPackReal(F): return (isData(F) and (F & DT_TYPE) == FF_PACKREAL) -def isASCII(F): return (isData(F) and (F & DT_TYPE) == FF_ASCI) -def isStruct(F): return (isData(F) and (F & DT_TYPE) == FF_STRU) -def isAlign(F): return (isData(F) and (F & DT_TYPE) == FF_ALIGN) - -# -# Bits for CODE bytes -# -MS_CODE = idaapi.MS_CODE & 0xFFFFFFFF -FF_FUNC = idaapi.FF_FUNC & 0xFFFFFFFF # function start? -FF_IMMD = idaapi.FF_IMMD & 0xFFFFFFFF # Has Immediate value ? -FF_JUMP = idaapi.FF_JUMP & 0xFFFFFFFF # Has jump table - -# -# Loader flags -# -NEF_SEGS = idaapi.NEF_SEGS # Create segments -NEF_RSCS = idaapi.NEF_RSCS # Load resources -NEF_NAME = idaapi.NEF_NAME # Rename entries -NEF_MAN = idaapi.NEF_MAN # Manual load -NEF_FILL = idaapi.NEF_FILL # Fill segment gaps -NEF_IMPS = idaapi.NEF_IMPS # Create imports section -NEF_TIGHT = idaapi.NEF_TIGHT # Don't align segments (OMF) -NEF_FIRST = idaapi.NEF_FIRST # This is the first file loaded -NEF_CODE = idaapi.NEF_CODE # for load_binary_file: -NEF_RELOAD = idaapi.NEF_RELOAD # reload the file at the same place: -NEF_FLAT = idaapi.NEF_FLAT # Autocreated FLAT group (PE) - -# List of built-in functions -# -------------------------- -# -# The following conventions are used in this list: -# 'ea' is a linear address -# 'success' is 0 if a function failed, 1 otherwise -# 'void' means that function returns no meaningful value (always 0) -# -# All function parameter conversions are made automatically. -# -# ---------------------------------------------------------------------------- -# M I S C E L L A N E O U S -# ---------------------------------------------------------------------------- -def IsString(var): raise NotImplementedError, "this function is not needed in Python" -def IsLong(var): raise NotImplementedError, "this function is not needed in Python" -def IsFloat(var): raise NotImplementedError, "this function is not needed in Python" - -def MK_FP(seg, off): - """ - Return value of expression: ((seg<<4) + off) - """ - return (seg << 4) + off - -def form(format, *args): - raise DeprecatedIDCError, "form() is deprecated. Use python string operations instead." - -def substr(s, x1, x2): - raise DeprecatedIDCError, "substr() is deprecated. Use python string operations instead." - -def strstr(s1, s2): - raise DeprecatedIDCError, "strstr() is deprecated. Use python string operations instead." - -def strlen(s): - raise DeprecatedIDCError, "strlen() is deprecated. Use python string operations instead." - -def xtol(s): - raise DeprecatedIDCError, "xtol() is deprecated. Use python long() instead." - - -def atoa(ea): - """ - Convert address value to a string - Return address in the form 'seg000:1234' - (the same as in line prefixes) - - @param ea: address to format - """ - segname = SegName(ea) - - if segname == "": - segname = "0" - - return "%s:%X" % (segname, ea) - - -def ltoa(n, radix): - raise DeprecatedIDCError, "ltoa() is deprecated. Use python string operations instead." - -def atol(s): - raise DeprecatedIDCError, "atol() is deprecated. Use python long() instead." - - -def rotate_left(value, count, nbits, offset): - """ - Rotate a value to the left (or right) - - @param value: value to rotate - @param count: number of times to rotate. negative counter means - rotate to the right - @param nbits: number of bits to rotate - @param offset: offset of the first bit to rotate - - @return: the value with the specified field rotated - all other bits are not modified - """ - assert offset >= 0, "offset must be >= 0" - assert nbits > 0, "nbits must be > 0" - - mask = 2**(offset+nbits) - 2**offset - tmp = value & mask - - if count > 0: - for x in xrange(count): - if (tmp >> (offset+nbits-1)) & 1: - tmp = (tmp << 1) | (1 << offset) - else: - tmp = (tmp << 1) - else: - for x in xrange(-count): - if (tmp >> offset) & 1: - tmp = (tmp >> 1) | (1 << (offset+nbits-1)) - else: - tmp = (tmp >> 1) - - value = (value-(value&mask)) | (tmp & mask) - - return value - - -def rotate_dword(x, count): return rotate_left(x, count, 32, 0) -def rotate_word(x, count): return rotate_left(x, count, 16, 0) -def rotate_byte(x, count): return rotate_left(x, count, 8, 0) - - -# AddHotkey return codes -IDCHK_OK = 0 # ok -IDCHK_ARG = -1 # bad argument(s) -IDCHK_KEY = -2 # bad hotkey name -IDCHK_MAX = -3 # too many IDC hotkeys - -def AddHotkey(hotkey, idcfunc): - """ - Add hotkey for IDC function - - @param hotkey: hotkey name ('a', "Alt-A", etc) - @param idcfunc: IDC function name - - @note: GUI version doesn't support hotkeys - - @return: None - """ - return idaapi.add_idc_hotkey(hotkey, idcfunc) - - -def DelHotkey(hotkey): - """ - Delete IDC function hotkey - - @param hotkey: hotkey code to delete - """ - return idaapi.del_idc_hotkey(hotkey) - - -def Jump(ea): - """ - Move cursor to the specifed linear address - - @param ea: linear address - """ - return idaapi.jumpto(ea) - - -def Wait(): - """ - Process all entries in the autoanalysis queue - Wait for the end of autoanalysis - - @note: This function will suspend execution of the calling script - till the autoanalysis queue is empty. - """ - return idaapi.autoWait() - - -def CompileEx(input, isfile): - """ - Compile an IDC script - - The input should not contain functions that are - currently executing - otherwise the behaviour of the replaced - functions is undefined. - - @param input: if isfile != 0, then this is the name of file to compile - otherwise it holds the text to compile - @param isfile: specify if 'input' holds a filename or the expression itself - - @return: 0 - ok, otherwise it returns an error message - """ - if isfile: - res = idaapi.Compile(input) - else: - res = idaapi.CompileLine(input) - - if res: - return res - else: - return 0 - - -def Eval(expr): - """ - Evaluate an IDC expression - - @param expr: an expression - - @return: the expression value. If there are problems, the returned value will be "IDC_FAILURE: xxx" - where xxx is the error description - - @note: Python implementation evaluates IDC only, while IDC can call other registered languages - """ - rv = idaapi.idc_value_t() - - err = idaapi.calc_idc_expr(BADADDR, expr, rv) - if err: - return "IDC_FAILURE: "+err - else: - if rv.vtype == '\x01': # VT_STR - return rv.str - elif rv.vtype == '\x02': # long - return rv.num - elif rv.vtype == '\x07': # VT_STR2 - return rv.c_str() - else: - raise NotImplementedError, "Eval() supports only expressions returning strings or longs" - - -def EVAL_FAILURE(code): - """ - Check the result of Eval() for evaluation failures - - @param code: result of Eval() - - @return: True if there was an evaluation error - """ - return type(code) == types.StringType and code.startswith("IDC_FAILURE: ") - - -def SaveBase(idbname, flags=0): - """ - Save current database to the specified idb file - - @param idbname: name of the idb file. if empty, the current idb - file will be used. - @param flags: DBFL_BAK or 0 - """ - if len(idbname)==0: - idbname = idaapi.cvar.database_idb - saveflags = idaapi.cvar.database_flags - if flags & DBFL_BAK: - idaapi.cvar.database_flags |= DBFL_BAK - else: - idaapi.cvar.database_flags &= ~DBFL_BAK - res = idaapi.save_database(idbname, 0) - idaapi.cvar.database_flags = saveflags - return res - -DBFL_BAK = 0x04 # create backup file - - -def Exit(code): - """ - Stop execution of IDC program, close the database and exit to OS - - @param code: code to exit with. - - @return: - - """ - idaapi.qexit(code) - - -def Exec(command): - """ - Execute an OS command. - - @param command: command line to execute - - @return: error code from OS - - @note: - IDA will wait for the started program to finish. - In order to start the command in parallel, use OS methods. - For example, you may start another program in parallel using - "start" command. - """ - return os.system(command) - - -def Sleep(milliseconds): - """ - Sleep the specified number of milliseconds - This function suspends IDA for the specified amount of time - - @param milliseconds: time to sleep - """ - time.sleep(float(milliseconds)/1000) - - -def RunPlugin(name, arg): - """ - Load and run a plugin - - @param name: The plugin name is a short plugin name without an extension - @param arg: integer argument - - @return: 0 if could not load the plugin, 1 if ok - """ - return idaapi.load_and_run_plugin(name, arg) - - -def ApplySig(name): - """ - Load (plan to apply) a FLIRT signature file - - @param name: signature name without path and extension - - @return: 0 if could not load the signature file, !=0 otherwise - """ - return idaapi.plan_to_apply_idasgn(name) - - -#---------------------------------------------------------------------------- -# C H A N G E P R O G R A M R E P R E S E N T A T I O N -#---------------------------------------------------------------------------- - - -def DeleteAll(): - """ - Delete all segments, instructions, comments, i.e. everything - except values of bytes. - """ - ea = idaapi.cvar.inf.minEA - - # Brute-force nuke all info from all the heads - while ea != BADADDR and ea <= idaapi.cvar.inf.maxEA: - idaapi.del_local_name(ea) - idaapi.del_global_name(ea) - func = idaapi.get_func(ea) - if func: - idaapi.del_func_cmt(func, False) - idaapi.del_func_cmt(func, True) - idaapi.del_func(ea) - idaapi.del_hidden_area(ea) - seg = idaapi.getseg(ea) - if seg: - idaapi.del_segment_cmt(seg, False) - idaapi.del_segment_cmt(seg, True) - idaapi.del_segm(ea, idaapi.SEGDEL_KEEP | idaapi.SEGDEL_SILENT) - - ea = idaapi.next_head(ea, idaapi.cvar.inf.maxEA) - - -def MakeCode(ea): - """ - Create an instruction at the specified address - - @param ea: linear address - - @return: 0 - can not create an instruction (no such opcode, the instruction - would overlap with existing items, etc) otherwise returns length of the - instruction in bytes - """ - return idaapi.create_insn(ea) - - -def AnalyzeArea(sEA, eEA): - """ - Perform full analysis of the area - - @param sEA: starting linear address - @param eEA: ending linear address (excluded) - - @return: 1-ok, 0-Ctrl-Break was pressed. - """ - return idaapi.analyze_area(sEA, eEA) - - -def MakeNameEx(ea, name, flags): - """ - Rename an address - - @param ea: linear address - @param name: new name of address. If name == "", then delete old name - @param flags: combination of SN_... constants - - @return: 1-ok, 0-failure - """ - return idaapi.set_name(ea, name, flags) - -SN_CHECK = idaapi.SN_CHECK # Fail if the name contains invalid - # characters - # If this bit is clear, all invalid chars - # (those !is_ident_char()) will be replaced - # by SubstChar (usually '_') - # List of valid characters is defined in - # ida.cfg -SN_NOCHECK = idaapi.SN_NOCHECK # Replace invalid chars with SubstChar -SN_PUBLIC = idaapi.SN_PUBLIC # if set, make name public -SN_NON_PUBLIC = idaapi.SN_NON_PUBLIC # if set, make name non-public -SN_WEAK = idaapi.SN_WEAK # if set, make name weak -SN_NON_WEAK = idaapi.SN_NON_WEAK # if set, make name non-weak -SN_AUTO = idaapi.SN_AUTO # if set, make name autogenerated -SN_NON_AUTO = idaapi.SN_NON_AUTO # if set, make name non-autogenerated -SN_NOLIST = idaapi.SN_NOLIST # if set, exclude name from the list - # if not set, then include the name into - # the list (however, if other bits are set, - # the name might be immediately excluded - # from the list) -SN_NOWARN = idaapi.SN_NOWARN # don't display a warning if failed -SN_LOCAL = idaapi.SN_LOCAL # create local name. a function should exist. - # local names can't be public or weak. - # also they are not included into the list - # of names they can't have dummy prefixes - -def MakeComm(ea, comment): - """ - Set an indented regular comment of an item - - @param ea: linear address - @param comment: comment string - - @return: None - """ - return idaapi.set_cmt(ea, comment, 0) - - -def MakeRptCmt(ea, comment): - """ - Set an indented repeatable comment of an item - - @param ea: linear address - @param comment: comment string - - @return: None - """ - return idaapi.set_cmt(ea, comment, 1) - - -def MakeArray(ea, nitems): - """ - Create an array. - - @param ea: linear address - @param nitems: size of array in items - - @note: This function will create an array of the items with the same type as - the type of the item at 'ea'. If the byte at 'ea' is undefined, then - this function will create an array of bytes. - """ - flags = idaapi.getFlags(ea) - - if idaapi.isUnknown(flags): - flags = idaapi.FF_BYTE - - if idaapi.isStruct(flags): - ti = idaapi.opinfo_t() - assert idaapi.get_typeinfo(ea, 0, flags, ti), "get_typeinfo() failed" - itemsize = idaapi.get_data_elsize(ea, flags, ti) - tid = ti.tid - else: - itemsize = idaapi.get_item_size(ea) - tid = BADADDR - - return idaapi.do_data_ex(ea, flags, itemsize*nitems, tid) - - -def MakeStr(ea, endea): - """ - Create a string. - - This function creates a string (the string type is determined by the - value of GetLongPrm(INF_STRTYPE)) - - @param ea: linear address - @param endea: ending address of the string (excluded) - if endea == BADADDR, then length of string will be calculated - by the kernel - - @return: 1-ok, 0-failure - - @note: The type of an existing string is returned by GetStringType() - """ - return idaapi.make_ascii_string(ea, endea - ea, GetLongPrm(INF_STRTYPE)) - - -def MakeData(ea, flags, size, tid): - """ - Create a data item at the specified address - - @param ea: linear address - @param flags: FF_BYTE..FF_PACKREAL - @param size: size of item in bytes - @param tid: for FF_STRU the structure id - - @return: 1-ok, 0-failure - """ - return idaapi.do_data_ex(ea, flags, size, tid) - - -def MakeByte(ea): - """ - Convert the current item to a byte - - @param ea: linear address - - @return: 1-ok, 0-failure - """ - return idaapi.doByte(ea, 1) - - -def MakeWord(ea): - """ - Convert the current item to a word (2 bytes) - - @param ea: linear address - - @return: 1-ok, 0-failure - """ - return idaapi.doWord(ea, 2) - - -def MakeDword(ea): - """ - Convert the current item to a double word (4 bytes) - - @param ea: linear address - - @return: 1-ok, 0-failure - """ - return idaapi.doDwrd(ea, 4) - - -def MakeQword(ea): - """ - Convert the current item to a quadro word (8 bytes) - - @param ea: linear address - - @return: 1-ok, 0-failure - """ - return idaapi.doQwrd(ea, 8) - - -def MakeOword(ea): - """ - Convert the current item to a octa word (16 bytes) - - @param ea: linear address - - @return: 1-ok, 0-failure - """ - return idaapi.doOwrd(ea, 16) - - -def MakeFloat(ea): - """ - Convert the current item to a floating point (4 bytes) - - @param ea: linear address - - @return: 1-ok, 0-failure - """ - return idaapi.doFloat(ea, 4) - - -def MakeDouble(ea): - """ - Convert the current item to a double floating point (8 bytes) - - @param ea: linear address - - @return: 1-ok, 0-failure - """ - return idaapi.doDouble(ea, 8) - - -def MakePackReal(ea): - """ - Convert the current item to a packed real (10 or 12 bytes) - - @param ea: linear address - - @return: 1-ok, 0-failure - """ - return idaapi.doPackReal(ea, idaapi.ph_get_tbyte_size()) - - -def MakeTbyte(ea): - """ - Convert the current item to a tbyte (10 or 12 bytes) - - @param ea: linear address - - @return: 1-ok, 0-failure - """ - return idaapi.doTbyt(ea, idaapi.ph_get_tbyte_size()) - - -def MakeStructEx(ea, size, strname): - """ - Convert the current item to a structure instance - - @param ea: linear address - @param size: structure size in bytes. -1 means that the size - will be calculated automatically - @param strname: name of a structure type - - @return: 1-ok, 0-failure - """ - strid = idaapi.get_struc_id(strname) - - if size == -1: - size = idaapi.get_struc_size(strid) - - return idaapi.doStruct(ea, size, strid) - - -def MakeAlign(ea, count, align): - """ - Convert the current item to an alignment directive - - @param ea: linear address - @param count: number of bytes to convert - @param align: 0 or 1..32 - if it is 0, the correct alignment will be calculated - by the kernel - - @return: 1-ok, 0-failure - """ - return idaapi.doAlign(ea, count, align) - - -def MakeLocal(start, end, location, name): - """ - Create a local variable - - @param start: start of address range for the local variable - @param end: end of address range for the local variable - @param location: the variable location in the "[bp+xx]" form where xx is - a number. The location can also be specified as a - register name. - @param name: name of the local variable - - @return: 1-ok, 0-failure - - @note: For the stack variables the end address is ignored. - If there is no function at 'start' then this function. - will fail. - """ - func = idaapi.get_func(start) - - if not func: - return 0 - - # Find out if location is in the [bp+xx] form - r = re.compile("\[([a-z]+)([-+][0-9a-fx]+)", re.IGNORECASE) - m = r.match(location) - - if m: - # Location in the form of [bp+xx] - register = idaapi.str2reg(m.group(1)) - offset = int(m.group(2), 0) - frame = idaapi.get_frame(func) - - if register == -1 or not frame: - return 0 - - offset += func.frsize - member = idaapi.get_member(frame, offset) - - if member: - # Member already exists, rename it - if idaapi.set_member_name(frame, offset, name): - return 1 - else: - return 0 - else: - # No member at the offset, create a new one - if idaapi.add_struc_member(frame, - name, - offset, - idaapi.byteflag(), - None, 1) == 0: - return 1 - else: - return 0 - else: - # Location as simple register name - return idaapi.add_regvar(func, start, end, location, name, None) - - -def MakeUnkn(ea, flags): - """ - Convert the current item to an explored item - - @param ea: linear address - @param flags: combination of DOUNK_* constants - - @return: None - """ - return idaapi.do_unknown(ea, flags) - - -def MakeUnknown(ea, size, flags): - """ - Convert the current item to an explored item - - @param ea: linear address - @param size: size of the range to undefine (for MakeUnknown) - @param flags: combination of DOUNK_* constants - - @return: None - """ - return idaapi.do_unknown_range(ea, size, flags) - - -DOUNK_SIMPLE = idaapi.DOUNK_SIMPLE # simply undefine the specified item -DOUNK_EXPAND = idaapi.DOUNK_EXPAND # propogate undefined items, for example - # if removing an instruction removes all - # references to the next instruction, then - # plan to convert to unexplored the next - # instruction too. -DOUNK_DELNAMES = idaapi.DOUNK_DELNAMES # delete any names at the specified address(es) - - -def SetArrayFormat(ea, flags, litems, align): - """ - Set array representation format - - @param ea: linear address - @param flags: combination of AP_... constants or 0 - @param litems: number of items per line. 0 means auto - @param align: element alignment - - -1: do not align - - 0: automatic alignment - - other values: element width - - @return: 1-ok, 0-failure - """ - return Eval("SetArrayFormat(0x%X, 0x%X, %d, %d)"%(ea, flags, litems, align)) - -AP_ALLOWDUPS = 0x00000001L # use 'dup' construct -AP_SIGNED = 0x00000002L # treats numbers as signed -AP_INDEX = 0x00000004L # display array element indexes as comments -AP_ARRAY = 0x00000008L # reserved (this flag is not stored in database) -AP_IDXBASEMASK = 0x000000F0L # mask for number base of the indexes -AP_IDXDEC = 0x00000000L # display indexes in decimal -AP_IDXHEX = 0x00000010L # display indexes in hex -AP_IDXOCT = 0x00000020L # display indexes in octal -AP_IDXBIN = 0x00000030L # display indexes in binary - -def OpBinary(ea, n): - """ - Convert an operand of the item (instruction or data) to a binary number - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - - @return: 1-ok, 0-failure - - @note: the data items use only the type of the first operand - """ - return idaapi.op_bin(ea, n) - - -def OpOctal(ea, n): - """ - Convert an operand of the item (instruction or data) to an octal number - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - """ - return idaapi.op_oct(ea, n) - - -def OpDecimal(ea, n): - """ - Convert an operand of the item (instruction or data) to a decimal number - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - """ - return idaapi.op_dec(ea, n) - - -def OpHex(ea, n): - """ - Convert an operand of the item (instruction or data) to a hexadecimal number - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - """ - return idaapi.op_hex(ea, n) - - -def OpChr(ea, n): - """ - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - """ - return idaapi.op_chr(ea, n) - - -def OpOff(ea, n, base): - """ - Convert operand to an offset - (for the explanations of 'ea' and 'n' please see OpBinary()) - - Example: - ======== - - seg000:2000 dw 1234h - - and there is a segment at paragraph 0x1000 and there is a data item - within the segment at 0x1234: - - seg000:1234 MyString db 'Hello, world!',0 - - Then you need to specify a linear address of the segment base to - create a proper offset: - - OpOff(["seg000",0x2000],0,0x10000); - - and you will have: - - seg000:2000 dw offset MyString - - Motorola 680x0 processor have a concept of "outer offsets". - If you want to create an outer offset, you need to combine number - of the operand with the following bit: - - Please note that the outer offsets are meaningful only for - Motorola 680x0. - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - @param base: base of the offset as a linear address - If base == BADADDR then the current operand becomes non-offset - """ - return idaapi.set_offset(ea, n, base) - - -OPND_OUTER = idaapi.OPND_OUTER # outer offset base - - -def OpOffEx(ea, n, reftype, target, base, tdelta): - """ - Convert operand to a complex offset expression - This is a more powerful version of OpOff() function. - It allows to explicitly specify the reference type (off8,off16, etc) - and the expression target with a possible target delta. - The complex expressions are represented by IDA in the following form: - - target + tdelta - base - - If the target is not present, then it will be calculated using - - target = operand_value - tdelta + base - - The target must be present for LOW.. and HIGH.. reference types - - @param ea: linear address of the instruction/data - @param n: number of operand to convert (the same as in OpOff) - @param reftype: one of REF_... constants - @param target: an explicitly specified expression target. if you don't - want to specify it, use -1. Please note that LOW... and - HIGH... reference type requre the target. - @param base: the offset base (a linear address) - @param tdelta: a displacement from the target which will be displayed - in the expression. - - @return: success (boolean) - """ - return idaapi.op_offset(ea, n, reftype, target, base, tdelta) - - -REF_OFF8 = idaapi.REF_OFF8 # 8bit full offset -REF_OFF16 = idaapi.REF_OFF16 # 16bit full offset -REF_OFF32 = idaapi.REF_OFF32 # 32bit full offset -REF_LOW8 = idaapi.REF_LOW8 # low 8bits of 16bit offset -REF_LOW16 = idaapi.REF_LOW16 # low 16bits of 32bit offset -REF_HIGH8 = idaapi.REF_HIGH8 # high 8bits of 16bit offset -REF_HIGH16 = idaapi.REF_HIGH16 # high 16bits of 32bit offset -REF_VHIGH = idaapi.REF_VHIGH # high ph.high_fixup_bits of 32bit offset (processor dependent) -REF_VLOW = idaapi.REF_VLOW # low (32-ph.high_fixup_bits) of 32bit offset (processor dependent) -REF_OFF64 = idaapi.REF_OFF64 # 64bit full offset -REFINFO_RVA = 0x10 # based reference (rva) -REFINFO_PASTEND = 0x20 # reference past an item it may point to an nonexistitng - # do not destroy alignment dirs -REFINFO_NOBASE = 0x80 # offset base is a number - # that base have be any value - # nb: base xrefs are created only if base - # points to the middle of a segment - - -def OpSeg(ea, n): - """ - Convert operand to a segment expression - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - """ - return idaapi.op_seg(ea, n) - - -def OpNumber(ea, n): - """ - Convert operand to a number (with default number base, radix) - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - """ - return idaapi.op_num(ea, n) - - -def OpFloat(ea, n): - """ - Convert operand to a floating-point number - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - - @return: 1-ok, 0-failure - """ - return idaapi.op_flt(ea, n) - - -def OpAlt(ea, n, opstr): - """ - Specify operand represenation manually. - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - @param opstr: a string represenation of the operand - - @note: IDA will not check the specified operand, it will simply display - it instead of the orginal representation of the operand. - """ - return idaapi.set_forced_operand(ea, n, opstr) - - -def OpSign(ea, n): - """ - Change sign of the operand - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - """ - return idaapi.toggle_sign(ea, n) - - -def OpNot(ea, n): - """ - Toggle the bitwise not operator for the operand - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - """ - idaapi.toggle_bnot(ea, n) - return True - - -def OpEnumEx(ea, n, enumid, serial): - """ - Convert operand to a symbolic constant - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - @param enumid: id of enumeration type - @param serial: serial number of the constant in the enumeration - The serial numbers are used if there are more than - one symbolic constant with the same value in the - enumeration. In this case the first defined constant - get the serial number 0, then second 1, etc. - There could be 256 symbolic constants with the same - value in the enumeration. - """ - return idaapi.op_enum(ea, n, enumid, serial) - - -def OpStroffEx(ea, n, strid, delta): - """ - Convert operand to an offset in a structure - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - @param strid: id of a structure type - @param delta: struct offset delta. usually 0. denotes the difference - between the structure base and the pointer into the structure. - - """ - path = idaapi.tid_array(1) - path[0] = strid - return idaapi.op_stroff(ea, n, path.cast(), 1, delta) - - -def OpStkvar(ea, n): - """ - Convert operand to a stack variable - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - """ - return idaapi.op_stkvar(ea, n) - - -def OpHigh(ea, n, target): - """ - Convert operand to a high offset - High offset is the upper 16bits of an offset. - This type is used by TMS320C6 processors (and probably by other - RISC processors too) - - @param ea: linear address - @param n: number of operand - - 0 - the first operand - - 1 - the second, third and all other operands - - -1 - all operands - @param target: the full value (all 32bits) of the offset - """ - return idaapi.op_offset(ea, n, idaapi.REF_HIGH16, target) - - -def MakeVar(ea): - """ - Mark the location as "variable" - - @param ea: address to mark - - @return: None - - @note: All that IDA does is to mark the location as "variable". - Nothing else, no additional analysis is performed. - This function may disappear in the future. - """ - idaapi.doVar(ea, 1) - - -def ExtLinA(ea, n, line): - """ - Specify an additional line to display before the generated ones. - - @param ea: linear address - @param n: number of anterior additioal line (0..MAX_ITEM_LINES) - @param line: the line to display - - @return: None - - @note: IDA displays additional lines from number 0 up to the first unexisting - additional line. So, if you specify additional line #150 and there is no - additional line #149, your line will not be displayed. MAX_ITEM_LINES is - defined in IDA.CFG - """ - idaapi.ExtraUpdate(ea, line, idaapi.E_PREV + n) - - -def ExtLinB(ea, n, line): - """ - Specify an additional line to display after the generated ones. - - @param ea: linear address - @param n: number of posterior additioal line (0..MAX_ITEM_LINES) - @param line: the line to display - - @return: None - - @note: IDA displays additional lines from number 0 up to the first - unexisting additional line. So, if you specify additional line #150 - and there is no additional line #149, your line will not be displayed. - MAX_ITEM_LINES is defined in IDA.CFG - """ - idaapi.ExtraUpdate(ea, line, idaapi.E_NEXT + n) - - -def DelExtLnA(ea, n): - """ - Delete an additional anterior line - - @param ea: linear address - @param n: number of anterior additioal line (0..500) - - @return: None - """ - idaapi.ExtraDel(ea, idaapi.E_PREV + n) - - -def DelExtLnB(ea, n): - """ - Delete an additional posterior line - - @param ea: linear address - @param n: number of posterior additioal line (0..500) - - @return: None - """ - idaapi.ExtraDel(ea, idaapi.E_NEXT + n) - - -def SetManualInsn(ea, insn): - """ - Specify instruction represenation manually. - - @param ea: linear address - @param insn: a string represenation of the operand - - @note: IDA will not check the specified instruction, it will simply - display it instead of the orginal representation. - """ - return idaapi.set_manual_insn(ea, insn) - - -def GetManualInsn(ea): - """ - Get manual representation of instruction - - @param ea: linear address - - @note: This function returns value set by SetManualInsn earlier. - """ - return idaapi.get_manual_insn(ea) - - -def PatchDbgByte(ea,value): - """ - Change a byte in the debugged process memory only - - @param ea: address - @param value: new value of the byte - - @return: 1 if successful, 0 if not - """ - return idaapi.put_dbg_byte(ea, value) - - -def PatchByte(ea, value): - """ - Change value of a program byte - If debugger was active then the debugged process memory will be patched too - - @param ea: linear address - @param value: new value of the byte - - @return: 1 if successful, 0 if not - """ - return idaapi.patch_byte(ea, value) - - -def PatchWord(ea, value): - """ - Change value of a program word (2 bytes) - - @param ea: linear address - @param value: new value of the word - - @return: 1 if successful, 0 if not - """ - return idaapi.patch_word(ea, value) - - -def PatchDword(ea, value): - """ - Change value of a double word - - @param ea: linear address - @param value: new value of the double word - - @return: 1 if successful, 0 if not - """ - return idaapi.patch_long(ea, value) - - -def SetFlags(ea, flags): - """ - Set new value of flags - This function should not used be used directly if possible. - It changes properties of a program byte and if misused, may lead to - very-very strange results. - - @param ea: adress - @param flags: new flags value - """ - return idaapi.setFlags(ea, flags) - -def SetRegEx(ea, reg, value, tag): - """ - Set value of a segment register. - - @param ea: linear address - @param reg: name of a register, like "cs", "ds", "es", etc. - @param value: new value of the segment register. - @param tag: of SR_... constants - - @note: IDA keeps tracks of all the points where segment register change their - values. This function allows you to specify the correct value of a segment - register if IDA is not able to find the corrent value. - - See also SetReg() compatibility macro. - """ - reg = idaapi.str2reg(reg); - if reg >= 0: - return idaapi.splitSRarea1(ea, reg, value, tag) - else: - return False - -SR_inherit = 1 # value is inherited from the previous area -SR_user = 2 # value is specified by the user -SR_auto = 3 # value is determined by IDA -SR_autostart = 4 # as SR_auto for segment starting address - - -def AutoMark2(start, end, queuetype): - """ - Plan to perform an action in the future. - This function will put your request to a special autoanalysis queue. - Later IDA will retrieve the request from the queue and process - it. There are several autoanalysis queue types. IDA will process all - queries from the first queue and then switch to the second queue, etc. - """ - return idaapi.auto_mark_range(start, end, queuetype) - - -def AutoUnmark(start, end, queuetype): - """ - Remove range of addresses from a queue. - """ - return idaapi.autoUnmark(start, end, queuetype) - - -def AutoMark(ea,qtype): - """ - Plan to analyze an address - """ - return AutoMark2(ea,ea+1,qtype) - -AU_UNK = idaapi.AU_UNK # make unknown -AU_CODE = idaapi.AU_CODE # convert to instruction -AU_PROC = idaapi.AU_PROC # make function -AU_USED = idaapi.AU_USED # reanalyze -AU_LIBF = idaapi.AU_LIBF # apply a flirt signature (the current signature!) -AU_FINAL = idaapi.AU_FINAL # coagulate unexplored items - - -#---------------------------------------------------------------------------- -# P R O D U C E O U T P U T F I L E S -#---------------------------------------------------------------------------- - -def GenerateFile(filetype, path, ea1, ea2, flags): - """ - Generate an output file - - @param filetype: type of output file. One of OFILE_... symbols. See below. - @param path: the output file path (will be overwritten!) - @param ea1: start address. For some file types this argument is ignored - @param ea2: end address. For some file types this argument is ignored - @param flags: bit combination of GENFLG_... - - @returns: number of the generated lines. - -1 if an error occured - OFILE_EXE: 0-can't generate exe file, 1-ok - """ - f = idaapi.fopenWT(path) - - if f: - retval = idaapi.gen_file(filetype, f, ea1, ea2, flags) - idaapi.eclose(f) - return retval - else: - return -1 - - -# output file types: -OFILE_MAP = idaapi.OFILE_MAP -OFILE_EXE = idaapi.OFILE_EXE -OFILE_IDC = idaapi.OFILE_IDC -OFILE_LST = idaapi.OFILE_LST -OFILE_ASM = idaapi.OFILE_ASM -OFILE_DIF = idaapi.OFILE_DIF - -# output control flags: -GENFLG_MAPSEG = idaapi.GENFLG_MAPSEG # map: generate map of segments -GENFLG_MAPNAME = idaapi.GENFLG_MAPNAME # map: include dummy names -GENFLG_MAPDMNG = idaapi.GENFLG_MAPDMNG # map: demangle names -GENFLG_MAPLOC = idaapi.GENFLG_MAPLOC # map: include local names -GENFLG_IDCTYPE = idaapi.GENFLG_IDCTYPE # idc: gen only information about types -GENFLG_ASMTYPE = idaapi.GENFLG_ASMTYPE # asm&lst: gen information about types too -GENFLG_GENHTML = idaapi.GENFLG_GENHTML # asm&lst: generate html (gui version only) -GENFLG_ASMINC = idaapi.GENFLG_ASMINC # asm&lst: gen information only about types - -def GenFuncGdl(outfile, title, ea1, ea2, flags): - """ - Generate a flow chart GDL file - - @param outfile: output file name. GDL extension will be used - @param title: graph title - @param ea1: beginning of the area to flow chart - @param ea2: end of the area to flow chart. - @param flags: combination of CHART_... constants - - @note: If ea2 == BADADDR then ea1 is treated as an address within a function. - That function will be flow charted. - """ - return idaapi.gen_flow_graph(outfile, title, None, ea1, ea2, flags) - - -CHART_PRINT_NAMES = 0x1000 # print labels for each block? -CHART_GEN_GDL = 0x4000 # generate .gdl file (file extension is forced to .gdl) -CHART_WINGRAPH = 0x8000 # call wingraph32 to display the graph -CHART_NOLIBFUNCS = 0x0400 # don't include library functions in the graph - - -def GenCallGdl(outfile, title, flags): - """ - Generate a function call graph GDL file - - @param outfile: output file name. GDL extension will be used - @param title: graph title - @param flags: combination of CHART_GEN_GDL, CHART_WINGRAPH, CHART_NOLIBFUNCS - """ - return idaapi.gen_simple_call_chart(outfile, "Generating chart", title, flags) - - -#---------------------------------------------------------------------------- -# C O M M O N I N F O R M A T I O N -#---------------------------------------------------------------------------- -def GetIdaDirectory (): - """ - Get IDA directory - - This function returns the directory where IDA.EXE resides - """ - return idaapi.idadir() - - -def GetInputFile(): - """ - Get input file name - - This function returns name of the file being disassembled - """ - return idaapi.get_root_filename() - - -def GetInputFilePath(): - """ - Get input file path - - This function returns the full path of the file being disassembled - """ - return idaapi.get_input_file_path() - - -def SetInputFilePath(path): - """ - Set input file name - This function updates the file name that is stored in the database - It is used by the debugger and other parts of IDA - Use it when the database is moved to another location or when you - use remote debugging. - - @param path: new input file path - """ - return idaapi.set_root_filename(path) - - -def GetIdbPath(): - """ - Get IDB full path - - This function returns full path of the current IDB database - """ - return idaapi.cvar.database_idb - - -def GetInputMD5(): - """ - Return the MD5 hash of the input binary file - - @return: MD5 string or None on error - """ - ua=idaapi.uchar_array(16) - if idaapi.retrieve_input_file_md5(ua.cast()): - md5str="" - for i in range(16): - md5str += "%02x" % ua[i] - return md5str - else: - return None - - -def GetFlags(ea): - """ - Get internal flags - - @param ea: linear address - - @return: 32-bit value of internal flags. See start of IDC.IDC file - for explanations. - """ - return idaapi.getFlags(ea) - - -def IdbByte(ea): - """ - Get one byte (8-bit) of the program at 'ea' from the database even if the debugger is active - - @param ea: linear address - - @return: byte value. If the byte has no value then 0xFF is returned. - - @note: If the current byte size is different from 8 bits, then the returned value may have more 1's. - To check if a byte has a value, use this expr: hasValue(GetFlags(ea)) - """ - return idaapi.get_db_byte(ea) - - -def Byte(ea): - """ - Get value of program byte - - @param ea: linear address - - @return: value of byte. If byte has no value then returns 0xFF - If the current byte size is different from 8 bits, then the returned value - might have more 1's. - To check if a byte has a value, use functions hasValue(GetFlags(ea)) - """ - return idaapi.get_byte(ea) - - -def GetOriginalByte(ea): - """ - Get original value of program byte - - @param ea: linear address - - @return: the original value of byte before any patch applied to it - """ - return idaapi.get_original_byte(ea) - - -def Word(ea): - """ - Get value of program word (2 bytes) - - @param ea: linear address - - @return: the value of the word. If word has no value then returns 0xFFFF - If the current byte size is different from 8 bits, then the returned value - might have more 1's. - """ - return idaapi.get_word(ea) - - -def Dword(ea): - """ - Get value of program double word (4 bytes) - - @param ea: linear address - - @return: the value of the double word. If failed returns -1 - """ - return idaapi.get_long(ea) - - -def Qword(ea): - """ - Get value of program quadro word (8 bytes) - - @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" - - -def GetFloat(ea): - """ - Get value of a floating point number (4 bytes) - - @param ea: linear address - - @return: float - """ - tmp = idaapi.get_many_bytes(ea, 4) - return struct.unpack("f", tmp)[0] - - -def GetDouble(ea): - """ - Get value of a floating point number (8 bytes) - - @param ea: linear address - - @return: double - """ - tmp = idaapi.get_many_bytes(ea, 8) - return struct.unpack("d", tmp)[0] - - -def LocByName(name): - """ - Get linear address of a name - - @param name: name of program byte - - @return: address of the name - badaddr - no such name - """ - return idaapi.get_name_ea(BADADDR, name) - - -def LocByNameEx(fromaddr, name): - """ - Get linear address of a name - - @param fromaddr: the referring address. Allows to retrieve local label - addresses in functions. If a local name is not found, - then address of a global name is returned. - - @param name: name of program byte - - @return: address of the name (BADADDR - no such name) - - @note: Dummy names (like byte_xxxx where xxxx are hex digits) are parsed by this - function to obtain the address. The database is not consulted for them. - """ - return idaapi.get_name_ea(fromaddr, name) - - -def SegByBase(base): - """ - Get segment by segment base - - @param base: segment base paragraph or selector - - @return: linear address of the start of the segment or BADADDR - if no such segment - """ - sel = idaapi.find_selector(base) - seg = idaapi.get_segm_by_sel(sel) - - if seg: - return seg.startEA - else: - return BADADDR - - -def ScreenEA(): - """ - Get linear address of cursor - """ - return idaapi.get_screen_ea() - - -def GetCurrentLine(): - """ - Get the disassembly line at the cursor - - @return: string - """ - return idaapi.tag_remove(idaapi.get_curline()) - - -def SelStart(): - """ - Get start address of the selected area - returns BADADDR - the user has not selected an area - """ - selection, startaddr, endaddr = idaapi.read_selection() - - if selection == 1: - return startaddr - else: - return BADADDR - - -def SelEnd(): - """ - Get end address of the selected area - - @return: BADADDR - the user has not selected an area - """ - selection, startaddr, endaddr = idaapi.read_selection() - - if selection == 1: - return endaddr - else: - return BADADDR - - -def GetReg(ea, reg): - """ - Get value of segment register at the specified address - - @param ea: linear address - @param reg: name of segment register - - @return: the value of the segment register or -1 on error - - @note: The segment registers in 32bit program usually contain selectors, - so to get paragraph pointed by the segment register you need to - call AskSelector() function. - """ - reg = idaapi.str2reg(reg); - if reg >= 0: - return idaapi.getSR(ea, reg) - else: - return -1 - -def NextAddr(ea): - """ - Get next address in the program - - @param ea: linear address - - @return: BADADDR - the specified address in the last used address - """ - return idaapi.nextaddr(ea) - - -def PrevAddr(ea): - """ - Get previous address in the program - - @param ea: linear address - - @return: BADADDR - the specified address in the first address - """ - return idaapi.prevaddr(ea) - - -def NextHead(ea, maxea): - """ - Get next defined item (instruction or data) in the program - - @param ea: linear address to start search from - @param maxea: the search will stop at the address - maxea is not included in the search range - - @return: BADADDR - no (more) defined items - """ - return idaapi.next_head(ea, maxea) - - -def PrevHead(ea, minea): - """ - Get previous defined item (instruction or data) in the program - - @param ea: linear address to start search from - @param minea: the search will stop at the address - minea is included in the search range - - @return: BADADDR - no (more) defined items - """ - return idaapi.prev_head(ea, minea) - - -def NextNotTail(ea): - """ - Get next not-tail address in the program - This function searches for the next displayable address in the program. - The tail bytes of instructions and data are not displayable. - - @param ea: linear address - - @return: BADADDR - no (more) not-tail addresses - """ - return idaapi.next_not_tail(ea) - - -def PrevNotTail(ea): - """ - Get previous not-tail address in the program - This function searches for the previous displayable address in the program. - The tail bytes of instructions and data are not displayable. - - @param ea: linear address - - @return: BADADDR - no (more) not-tail addresses - """ - return idaapi.prev_not_tail(ea) - - -def ItemEnd(ea): - """ - Get address of the end of the item (instruction or data) - - @param ea: linear address - - @return: address past end of the item at 'ea' - """ - return idaapi.get_item_end(ea) - - -def ItemSize(ea): - """ - Get size of instruction or data item in bytes - - @param ea: linear address - - @return: 1..n - """ - return idaapi.get_item_end(ea) - ea - - -def NameEx(fromaddr, ea): - """ - Get visible name of program byte - - This function returns name of byte as it is displayed on the screen. - If a name contains illegal characters, IDA replaces them by the - substitution character during displaying. See IDA.CFG for the - definition of the substitution character. - - @param fromaddr: the referring address. May be BADADDR. - Allows to retrieve local label addresses in functions. - If a local name is not found, then a global name is - returned. - @param ea: linear address - - @return: "" - byte has no name - """ - name = idaapi.get_name(fromaddr, ea) - - if not name: - return "" - else: - return name - - -def GetTrueNameEx(fromaddr, ea): - """ - Get true name of program byte - - This function returns name of byte as is without any replacements. - - @param fromaddr: the referring address. May be BADADDR. - Allows to retrieve local label addresses in functions. - If a local name is not found, then a global name is returned. - @param ea: linear address - - @return: "" - byte has no name - """ - name = idaapi.get_true_name(fromaddr, ea) - - if not name: - return "" - else: - return name - - -def Demangle(name, disable_mask): - """ - Demangle a name - - @param name: name to demangle - @param disable_mask: a mask that tells how to demangle the name - it is a good idea to get this mask using - GetLongPrm(INF_SHORT_DN) or GetLongPrm(INF_LONG_DN) - - @return: a demangled name - If the input name cannot be demangled, returns None - """ - return idaapi.demangle_name(name, disable_mask) - - -def GetDisasm(ea): - """ - Get disassembly line - - @param ea: linear address of instruction - - @return: "" - no instruction at the specified location - - @note: this function may not return exactly the same mnemonics - as you see on the screen. - """ - text = idaapi.generate_disasm_line(ea) - if text: - return idaapi.tag_remove(text) - else: - return "" - - -def GetMnem(ea): - """ - Get instruction mnemonics - - @param ea: linear address of instruction - - @return: "" - no instruction at the specified location - - @note: this function may not return exactly the same mnemonics - as you see on the screen. - """ - res = idaapi.ua_mnem(ea) - - if not res: - return "" - else: - return res - - -def GetOpnd(ea, n): - """ - Get operand of an instruction - - @param ea: linear address of instruction - @param n: number of operand: - 0 - the first operand - 1 - the second operand - - @return: the current text representation of operand - """ - res = idaapi.ua_outop(ea, n) - - if not res: - return "" - else: - return idaapi.tag_remove(res) - - -def GetOpType(ea, n): - """ - Get type of instruction operand - - @param ea: linear address of instruction - @param n: number of operand: - 0 - the first operand - 1 - the second operand - - @return: any of o_* constants or -1 on error - """ - inslen = idaapi.decode_insn(ea) - - if inslen == 0: - return -1 - - insn = idaapi.get_current_instruction() - - if not insn: - return -1 - - op = idaapi.get_instruction_operand(insn, n) - - if not op: - return -1 - - return op.type - -o_void = 0 # No Operand ---------- -o_reg = 1 # General Register (al,ax,es,ds...) reg -o_mem = 2 # Direct Memory Reference (DATA) addr -o_phrase = 3 # Memory Ref [Base Reg + Index Reg] phrase -o_displ = 4 # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr -o_imm = 5 # Immediate Value value -o_far = 6 # Immediate Far Address (CODE) addr -o_near = 7 # Immediate Near Address (CODE) addr -o_idpspec0 = 8 # IDP specific type -o_idpspec1 = 9 # IDP specific type -o_idpspec2 = 10 # IDP specific type -o_idpspec3 = 11 # IDP specific type -o_idpspec4 = 12 # IDP specific type -o_idpspec5 = 13 # IDP specific type - -# x86 -o_trreg = o_idpspec0 # trace register -o_dbreg = o_idpspec1 # debug register -o_crreg = o_idpspec2 # control register -o_fpreg = o_idpspec3 # floating point register -o_mmxreg = o_idpspec4 # mmx register -o_xmmreg = o_idpspec5 # xmm register - -# arm -o_reglist = o_idpspec1 # Register list (for LDM/STM) -o_creglist = o_idpspec2 # Coprocessor register list (for CDP) -o_creg = o_idpspec3 # Coprocessor register (for LDC/STC) -o_fpreg = o_idpspec4 # Floating point register -o_fpreglist = o_idpspec5 # Floating point register list -o_text = (o_idpspec5+1) # Arbitrary text stored in the operand - -# ppc -o_spr = o_idpspec0 # Special purpose register -o_twofpr = o_idpspec1 # Two FPRs -o_shmbme = o_idpspec2 # SH & MB & ME -o_crf = o_idpspec3 # crfield x.reg -o_crb = o_idpspec4 # crbit x.reg -o_dcr = o_idpspec5 # Device control register - -def GetOperandValue(ea, n): - """ - Get number used in the operand - - This function returns an immediate number used in the operand - - @param ea: linear address of instruction - @param n: the operand number - - @return: value - operand is an immediate value => immediate value - operand has a displacement => displacement - operand is a direct memory ref => memory address - operand is a register => register number - operand is a register phrase => phrase number - otherwise => -1 - """ - inslen = idaapi.decode_insn(ea) - if inslen == 0: - return -1 - - insn = idaapi.get_current_instruction() - if not insn: - return -1 - - op = idaapi.get_instruction_operand(insn, n) - if not op: - return -1 - - if op.type in [ idaapi.o_mem, idaapi.o_far, idaapi.o_near, idaapi.o_displ ]: - value = op.addr - elif op.type == idaapi.o_reg: - value = op.reg - elif op.type == idaapi.o_imm: - value = op.value - elif op.type == idaapi.o_phrase: - value = op.phrase - else: - value = -1 - return value - - -def LineA(ea, num): - """ - Get anterior line - - @param ea: linear address - @param num: number of anterior line (0..MAX_ITEM_LINES) - MAX_ITEM_LINES is defined in IDA.CFG - - @return: anterior line string - """ - return idaapi.ExtraGet(ea, idaapi.E_PREV + num) - - -def LineB(ea, num): - """ - Get posterior line - - @param ea: linear address - @param num: number of posterior line (0..MAX_ITEM_LINES) - - @return: posterior line string - """ - return idaapi.ExtraGet(ea, idaapi.E_NEXT + num) - - -def GetCommentEx(ea, repeatable): - """ - Get regular indented comment - - @param ea: linear address - - @return: string or None if it fails - """ - return idaapi.get_cmt(ea, repeatable) - - -def CommentEx(ea, repeatable): GetCommentEx(ea, repeatable) - - -def AltOp(ea, n): - """ - Get manually entered operand string - - @param ea: linear address - @param n: number of operand: - 0 - the first operand - 1 - the second operand - - @return: string or None if it fails - """ - return idaapi.get_forced_operand(ea, n) - - -def GetString(ea, length = -1, strtype = idaapi.ASCSTR_TERMCHR): - """ - Get string contents - @param ea: linear address - @param length: string length. -1 means to calculate the max string length - @param strtype: the string type (one of ASCSTR_... constants) - - @return: string contents or empty string - """ - if length == -1: - length = idaapi.get_max_ascii_length(ea, strtype) - - return idaapi.get_ascii_contents(ea, length, strtype) - - -def GetStringType(ea): - """ - Get string type - - @param ea: linear address - - @return: One of ASCSTR_... constants - """ - ti = idaapi.opinfo_t() - - if idaapi.get_typeinfo(ea, 0, GetFlags(ea), ti): - return ti.strtype - else: - return None - -ASCSTR_C = idaapi.ASCSTR_TERMCHR # C-style ASCII string -ASCSTR_PASCAL = idaapi.ASCSTR_PASCAL # Pascal-style ASCII string (length byte) -ASCSTR_LEN2 = idaapi.ASCSTR_LEN2 # Pascal-style, length is 2 bytes -ASCSTR_UNICODE = idaapi.ASCSTR_UNICODE # Unicode string -ASCSTR_LEN4 = idaapi.ASCSTR_LEN4 # Pascal-style, length is 4 bytes -ASCSTR_ULEN2 = idaapi.ASCSTR_ULEN2 # Pascal-style Unicode, length is 2 bytes -ASCSTR_ULEN4 = idaapi.ASCSTR_ULEN4 # Pascal-style Unicode, length is 4 bytes -ASCSTR_LAST = idaapi.ASCSTR_LAST # Last string type - - -# The following functions search for the specified byte -# ea - address to start from -# flag is combination of the following bits - -# returns BADADDR - not found -def FindVoid (ea, flag): return idaapi.find_void(ea, flag) -def FindCode (ea, flag): return idaapi.find_code(ea, flag) -def FindData (ea, flag): return idaapi.find_data(ea, flag) -def FindUnexplored (ea, flag): return idaapi.find_unknown(ea, flag) -def FindExplored (ea, flag): return idaapi.find_defined(ea, flag) -def FindImmediate (ea, flag, value): return idaapi.find_imm(ea, flag, value) - -SEARCH_UP = idaapi.SEARCH_UP # search backward -SEARCH_DOWN = idaapi.SEARCH_DOWN # search forward -SEARCH_NEXT = idaapi.SEARCH_NEXT # start the search at the next/prev item -SEARCH_CASE = idaapi.SEARCH_CASE # search case-sensitive - # (only for bin&txt search) -SEARCH_REGEX = idaapi.SEARCH_REGEX # enable regular expressions (only for text) -SEARCH_NOBRK = idaapi.SEARCH_NOBRK # don't test ctrl-break -SEARCH_NOSHOW = idaapi.SEARCH_NOSHOW # don't display the search progress - -def FindText(ea, flag, y, x, searchstr): - """ - @param ea: start address - @param flag: combination of SEARCH_* flags - @param y: number of text line at ea to start from (0..MAX_ITEM_LINES) - @param x: coordinate in this line - @param searchstr: search string - - @return: ea of result or BADADDR if not found - """ - return idaapi.find_text(ea, y, x, searchstr, flag) - - -def FindBinary(ea, flag, searchstr, radix=16): - """ - @param ea: start address - @param flag: combination of SEARCH_* flags - @param searchstr: a string as a user enters it for Search Text in Core - @param radix: radix of the numbers (default=16) - - @return: ea of result or BADADDR if not found - - @note: Example: "41 42" - find 2 bytes 41h,42h (radix is 16) - """ - endea = flag & 1 and idaapi.cvar.inf.maxEA or idaapi.cvar.inf.minEA - return idaapi.find_binary(ea, endea, searchstr, radix, flag) - - -#---------------------------------------------------------------------------- -# G L O B A L S E T T I N G S M A N I P U L A T I O N -#---------------------------------------------------------------------------- -def ChangeConfig(directive): - """ - Parse one or more ida.cfg config directives - @param directive: directives to process, for example: PACK_DATABASE=2 - - @note: If the directives are erroneous, a fatal error will be generated. - The changes will be effective only for the current session. - """ - return Eval('ChangeConfig("%s")' % directive) - - -# The following functions allow you to set/get common parameters. -# Please note that not all parameters can be set directly. - -def GetLongPrm(offset): - """ - """ - val = _IDC_GetAttr(idaapi.cvar.inf, _INFMAP, offset) - if offset == INF_PROCNAME: - # procName is a character array - # strip it at the terminating zero - idx = val.find('\0') - if idx != -1: - val = val[:idx] - return val - -def GetShortPrm(offset): - return GetLongPrm(offset) - - -def GetCharPrm (offset): - return GetLongPrm(offset) - - -def SetLongPrm (offset, value): - """ - """ - if offset == INF_PROCNAME: - raise NotImplementedError, "Please use idaapi.set_processor_type() to change processor" - return _IDC_SetAttr(idaapi.cvar.inf, _INFMAP, offset, value) - - -def SetShortPrm(offset, value): - SetLongPrm(offset, value) - - -def SetCharPrm (offset, value): - SetLongPrm(offset, value) - - -INF_VERSION = 3 # short; Version of database -INF_PROCNAME = 5 # char[8]; Name of current processor -INF_LFLAGS = 13 # char; IDP-dependent flags -LFLG_PC_FPP = 0x01 # decode floating point processor - # instructions? -LFLG_PC_FLAT = 0x02 # Flat model? -LFLG_64BIT = 0x04 # 64-bit program? -LFLG_DBG_NOPATH = 0x08 # do not store input full path -LFLG_SNAPSHOT = 0x10 # is memory snapshot? - # in debugger process options -INF_DEMNAMES = 14 # char; display demangled names as: -DEMNAM_CMNT = 0 # comments -DEMNAM_NAME = 1 # regular names -DEMNAM_NONE = 2 # don't display -INF_FILETYPE = 15 # short; type of input file (see ida.hpp) -FT_EXE_OLD = 0 # MS DOS EXE File (obsolete) -FT_COM_OLD = 1 # MS DOS COM File (obsolete) -FT_BIN = 2 # Binary File -FT_DRV = 3 # MS DOS Driver -FT_WIN = 4 # New Executable (NE) -FT_HEX = 5 # Intel Hex Object File -FT_MEX = 6 # MOS Technology Hex Object File -FT_LX = 7 # Linear Executable (LX) -FT_LE = 8 # Linear Executable (LE) -FT_NLM = 9 # Netware Loadable Module (NLM) -FT_COFF = 10 # Common Object File Format (COFF) -FT_PE = 11 # Portable Executable (PE) -FT_OMF = 12 # Object Module Format -FT_SREC = 13 # R-records -FT_ZIP = 14 # ZIP file (this file is never loaded to IDA database) -FT_OMFLIB = 15 # Library of OMF Modules -FT_AR = 16 # ar library -FT_LOADER = 17 # file is loaded using LOADER DLL -FT_ELF = 18 # Executable and Linkable Format (ELF) -FT_W32RUN = 19 # Watcom DOS32 Extender (W32RUN) -FT_AOUT = 20 # Linux a.out (AOUT) -FT_PRC = 21 # PalmPilot program file -FT_EXE = 22 # MS DOS EXE File -FT_COM = 23 # MS DOS COM File -FT_AIXAR = 24 # AIX ar library -INF_FCORESIZ = 17 -INF_CORESTART = 21 -INF_OSTYPE = 25 # short; FLIRT: OS type the program is for -OSTYPE_MSDOS = 0x0001 -OSTYPE_WIN = 0x0002 -OSTYPE_OS2 = 0x0004 -OSTYPE_NETW = 0x0008 -INF_APPTYPE = 27 # short; FLIRT: Application type -APPT_CONSOLE = 0x0001 # console -APPT_GRAPHIC = 0x0002 # graphics -APPT_PROGRAM = 0x0004 # EXE -APPT_LIBRARY = 0x0008 # DLL -APPT_DRIVER = 0x0010 # DRIVER -APPT_1THREAD = 0x0020 # Singlethread -APPT_MTHREAD = 0x0040 # Multithread -APPT_16BIT = 0x0080 # 16 bit application -APPT_32BIT = 0x0100 # 32 bit application -INF_START_SP = 29 # long; SP register value at the start of - # program execution -INF_START_AF = 33 # short; Analysis flags: -AF_FIXUP = 0x0001 # Create offsets and segments using fixup info -AF_MARKCODE = 0x0002 # Mark typical code sequences as code -AF_UNK = 0x0004 # Delete instructions with no xrefs -AF_CODE = 0x0008 # Trace execution flow -AF_PROC = 0x0010 # Create functions if call is present -AF_USED = 0x0020 # Analyze and create all xrefs -AF_FLIRT = 0x0040 # Use flirt signatures -AF_PROCPTR = 0x0080 # Create function if data xref data->code32 exists -AF_JFUNC = 0x0100 # Rename jump functions as j_... -AF_NULLSUB = 0x0200 # Rename empty functions as nullsub_... -AF_LVAR = 0x0400 # Create stack variables -AF_TRACE = 0x0800 # Trace stack pointer -AF_ASCII = 0x1000 # Create ascii string if data xref exists -AF_IMMOFF = 0x2000 # Convert 32bit instruction operand to offset -AF_DREFOFF = 0x4000 # Create offset if data xref to seg32 exists -AF_FINAL = 0x8000 # Final pass of analysis -INF_START_IP = 35 # long; IP register value at the start of - # program execution -INF_BEGIN_EA = 39 # long; Linear address of program entry point -INF_MIN_EA = 43 # long; The lowest address used - # in the program -INF_MAX_EA = 47 # long; The highest address used - # in the program - = 1 -INF_OMIN_EA = 51 -INF_OMAX_EA = 55 -INF_LOW_OFF = 59 # long; low limit of voids -INF_HIGH_OFF = 63 # long; high limit of voids -INF_MAXREF = 67 # long; max xref depth -INF_ASCII_BREAK = 71 # char; ASCII line break symbol -INF_WIDE_HIGH_BYTE_FIRST = 72 -INF_INDENT = 73 # char; Indention for instructions -INF_COMMENT = 74 # char; Indention for comments -INF_XREFNUM = 75 # char; Number of references to generate - # = 0 - xrefs wont be generated at all -INF_ENTAB = 76 # char; Use '\t' chars in the output file? -INF_SPECSEGS = 77 -INF_VOIDS = 78 # char; Display void marks? -INF_SHOWAUTO = 80 # char; Display autoanalysis indicator? -INF_AUTO = 81 # char; Autoanalysis is enabled? -INF_BORDER = 82 # char; Generate borders? -INF_NULL = 83 # char; Generate empty lines? -INF_GENFLAGS = 84 # char; General flags: -INFFL_LZERO = 0x01 # generate leading zeroes in numbers -INFFL_LOADIDC = 0x04 # Loading an idc file t -INF_SHOWPREF = 85 # char; Show line prefixes? -INF_PREFSEG = 86 # char; line prefixes with segment name? -INF_ASMTYPE = 87 # char; target assembler number (0..n) -INF_BASEADDR = 88 # long; base paragraph of the program -INF_XREFS = 92 # char; xrefs representation: -SW_SEGXRF = 0x01 # show segments in xrefs? -SW_XRFMRK = 0x02 # show xref type marks? -SW_XRFFNC = 0x04 # show function offsets? -SW_XRFVAL = 0x08 # show xref values? (otherwise-"...") -INF_BINPREF = 93 # short; # of instruction bytes to show - # in line prefix -INF_CMTFLAG = 95 # char; comments: -SW_RPTCMT = 0x01 # show repeatable comments? -SW_ALLCMT = 0x02 # comment all lines? -SW_NOCMT = 0x04 # no comments at all -SW_LINNUM = 0x08 # show source line numbers -SW_MICRO = 0x10 # show microcode (if implemented) -INF_NAMETYPE = 96 # char; dummy names represenation type -NM_REL_OFF = 0 -NM_PTR_OFF = 1 -NM_NAM_OFF = 2 -NM_REL_EA = 3 -NM_PTR_EA = 4 -NM_NAM_EA = 5 -NM_EA = 6 -NM_EA4 = 7 -NM_EA8 = 8 -NM_SHORT = 9 -NM_SERIAL = 10 -INF_SHOWBADS = 97 # char; show bad instructions? - # an instruction is bad if it appears - # in the ash.badworks array - -INF_PREFFLAG = 98 # char; line prefix type: -PREF_SEGADR = 0x01 # show segment addresses? -PREF_FNCOFF = 0x02 # show function offsets? -PREF_STACK = 0x04 # show stack pointer? - -INF_PACKBASE = 99 # char; pack database? - -INF_ASCIIFLAGS = 100 # uchar; ascii flags -ASCF_GEN = 0x01 # generate ASCII names? -ASCF_AUTO = 0x02 # ASCII names have 'autogenerated' bit? -ASCF_SERIAL = 0x04 # generate serial names? -ASCF_COMMENT = 0x10 # generate auto comment for ascii references? -ASCF_SAVECASE = 0x20 # preserve case of ascii strings for identifiers - -INF_LISTNAMES = 101 # uchar; What names should be included in the list? -LN_NORMAL = 0x01 # normal names -LN_PUBLIC = 0x02 # public names -LN_AUTO = 0x04 # autogenerated names -LN_WEAK = 0x08 # weak names - -INF_ASCIIPREF = 102 # char[16];ASCII names prefix -INF_ASCIISERNUM = 118 # ulong; serial number -INF_ASCIIZEROES = 122 # char; leading zeroes -INF_MF = 126 # uchar; Byte order: 1==MSB first -INF_ORG = 127 # char; Generate 'org' directives? -INF_ASSUME = 128 # char; Generate 'assume' directives? -INF_CHECKARG = 129 # char; Check manual operands? -INF_START_SS = 130 # long; value of SS at the start -INF_START_CS = 134 # long; value of CS at the start -INF_MAIN = 138 # long; address of main() -INF_SHORT_DN = 142 # long; short form of demangled names -INF_LONG_DN = 146 # long; long form of demangled names - # see demangle.h for definitions -INF_DATATYPES = 150 # long; data types allowed in data carousel -INF_STRTYPE = 154 # long; current ascii string type - # is considered as several bytes: - # low byte: -ASCSTR_TERMCHR = 0 # Character-terminated ASCII string -ASCSTR_C = 0 # C-string, zero terminated -ASCSTR_PASCAL = 1 # Pascal-style ASCII string (length byte) -ASCSTR_LEN2 = 2 # Pascal-style, length is 2 bytes -ASCSTR_UNICODE = 3 # Unicode string -ASCSTR_LEN4 = 4 # Delphi string, length is 4 bytes -ASCSTR_ULEN2 = 5 # Pascal-style Unicode, length is 2 bytes -ASCSTR_ULEN4 = 6 # Pascal-style Unicode, length is 4 bytes - -# = 2nd byte - termination chracters for ASCSTR_TERMCHR: -#STRTERM1(strtype) ((strtype>>8)&0xFF) -# = 3d byte: -#STRTERM2(strtype) ((strtype>>16)&0xFF) - # The termination characters are kept in - # the = 2nd and 3d bytes of string type - # if the second termination character is - # '\0', then it is ignored. -INF_AF2 = 158 # ushort; Analysis flags 2 -AF2_JUMPTBL = 0x0001 # Locate and create jump tables -AF2_DODATA = 0x0002 # Coagulate data segs in final pass -AF2_HFLIRT = 0x0004 # Automatically hide library functions -AF2_STKARG = 0x0008 # Propagate stack argument information -AF2_REGARG = 0x0010 # Propagate register argument information -AF2_CHKUNI = 0x0020 # Check for unicode strings -AF2_SIGCMT = 0x0040 # Append a signature name comment for recognized anonymous library functions -AF2_SIGMLT = 0x0080 # Allow recognition of several copies of the same function -AF2_FTAIL = 0x0100 # Create function tails -AF2_DATOFF = 0x0200 # Automatically convert data to offsets -AF2_ANORET = 0x0400 # Perform 'no-return' analysis -AF2_VERSP = 0x0800 # Perform full stack pointer analysis -AF2_DOCODE = 0x1000 # Coagulate code segs at the final pass -AF2_TRFUNC = 0x2000 # Truncate functions upon code deletion -AF2_PURDAT = 0x4000 # Control flow to data segment is ignored -INF_NAMELEN = 160 # ushort; max name length (without zero byte) -INF_MARGIN = 162 # ushort; max length of data lines -INF_LENXREF = 164 # ushort; max length of line with xrefs -INF_LPREFIX = 166 # char[16];prefix of local names - # if a new name has this prefix, - # it will be automatically converted to a local name -INF_LPREFIXLEN = 182 # uchar; length of the lprefix -INF_COMPILER = 183 # uchar; compiler -COMP_MASK = 0x0F # mask to apply to get the pure compiler id -COMP_UNK = 0x00 # Unknown -COMP_MS = 0x01 # Visual C++ -COMP_BC = 0x02 # Borland C++ -COMP_WATCOM = 0x03 # Watcom C++ -COMP_GNU = 0x06 # GNU C++ -COMP_VISAGE = 0x07 # Visual Age C++ -COMP_BP = 0x08 # Delphi - -INF_MODEL = 184 # uchar; memory model & calling convention -INF_SIZEOF_INT = 185 # uchar; sizeof(int) -INF_SIZEOF_BOOL = 186 # uchar; sizeof(bool) -INF_SIZEOF_ENUM = 187 # uchar; sizeof(enum) -INF_SIZEOF_ALGN = 188 # uchar; default alignment -INF_SIZEOF_SHORT = 189 -INF_SIZEOF_LONG = 190 -INF_SIZEOF_LLONG = 191 -INF_CHANGE_COUNTER = 192 # database change counter; keeps track of byte and segment modifications -INF_SIZEOF_LDBL = 196 # uchar; sizeof(long double) - -# Redefine these offsets for 64-bit version -if __EA64__: - INF_CORESTART = 25 - INF_OSTYPE = 33 - INF_APPTYPE = 35 - INF_START_SP = 37 - INF_AF = 45 - INF_START_IP = 47 - INF_BEGIN_EA = 55 - INF_MIN_EA = 63 - INF_MAX_EA = 71 - INF_OMIN_EA = 79 - INF_OMAX_EA = 87 - INF_LOW_OFF = 95 - INF_HIGH_OFF = 103 - INF_MAXREF = 111 - INF_ASCII_BREAK = 119 - INF_WIDE_HIGH_BYTE_FIRST = 120 - INF_INDENT = 121 - INF_COMMENT = 122 - INF_XREFNUM = 123 - INF_ENTAB = 124 - INF_SPECSEGS = 125 - INF_VOIDS = 126 - INF_SHOWAUTO = 128 - INF_AUTO = 129 - INF_BORDER = 130 - INF_NULL = 131 - INF_GENFLAGS = 132 - INF_SHOWPREF = 133 - INF_PREFSEG = 134 - INF_ASMTYPE = 135 - INF_BASEADDR = 136 - INF_XREFS = 144 - INF_BINPREF = 145 - INF_CMTFLAG = 147 - INF_NAMETYPE = 148 - INF_SHOWBADS = 149 - INF_PREFFLAG = 150 - INF_PACKBASE = 151 - INF_ASCIIFLAGS = 152 - INF_LISTNAMES = 153 - INF_ASCIIPREF = 154 - INF_ASCIISERNUM = 170 - INF_ASCIIZEROES = 178 - INF_MF = 182 - INF_ORG = 183 - INF_ASSUME = 184 - INF_CHECKARG = 185 - INF_START_SS = 186 - INF_START_CS = 194 - INF_MAIN = 202 - INF_SHORT_DN = 210 - INF_LONG_DN = 218 - INF_DATATYPES = 226 - INF_STRTYPE = 234 - INF_AF2 = 242 - INF_NAMELEN = 244 - INF_MARGIN = 246 - INF_LENXREF = 248 - INF_LPREFIX = 250 - INF_LPREFIXLEN = 266 - INF_COMPILER = 267 - INF_MODEL = 268 - INF_SIZEOF_INT = 269 - INF_SIZEOF_BOOL = 270 - INF_SIZEOF_ENUM = 271 - INF_SIZEOF_ALGN = 272 - INF_SIZEOF_SHORT = 273 - INF_SIZEOF_LONG = 274 - INF_SIZEOF_LLONG = 275 - INF_CHANGE_COUNTER = 276 - INF_SIZEOF_LBDL = 280 - -_INFMAP = { -INF_VERSION : (False, 'version'), # short; Version of database -INF_PROCNAME : (False, 'procName'), # char[8]; Name of current processor -INF_LFLAGS : (False, 'lflags'), # char; IDP-dependent flags -INF_DEMNAMES : (False, 'demnames'), # char; display demangled names as: -INF_FILETYPE : (False, 'filetype'), # short; type of input file (see ida.hpp) -INF_FCORESIZ : (False, 'fcoresize'), -INF_CORESTART : (False, 'corestart'), -INF_OSTYPE : (False, 'ostype'), # short; FLIRT: OS type the program is for -INF_APPTYPE : (False, 'apptype'), # short; FLIRT: Application type -INF_START_SP : (False, 'startSP'), # long; SP register value at the start of -INF_START_AF : (False, 'af'), # short; Analysis flags: -INF_START_IP : (False, 'startIP'), # long; IP register value at the start of -INF_BEGIN_EA : (False, 'beginEA'), # long; Linear address of program entry point -INF_MIN_EA : (False, 'minEA'), # long; The lowest address used -INF_MAX_EA : (False, 'maxEA'), # long; The highest address used -INF_OMIN_EA : (False, 'ominEA'), -INF_OMAX_EA : (False, 'omaxEA'), -INF_LOW_OFF : (False, 'lowoff'), # long; low limit of voids -INF_HIGH_OFF : (False, 'highoff'), # long; high limit of voids -INF_MAXREF : (False, 'maxref'), # long; max xref depth -INF_ASCII_BREAK : (False, 'ASCIIbreak'), # char; ASCII line break symbol -INF_WIDE_HIGH_BYTE_FIRST : (False, 'wide_high_byte_first'), -INF_INDENT : (False, 'indent'), # char; Indention for instructions -INF_COMMENT : (False, 'comment'), # char; Indention for comments -INF_XREFNUM : (False, 'xrefnum'), # char; Number of references to generate -INF_ENTAB : (False, 's_entab'), # char; Use '\t' chars in the output file? -INF_SPECSEGS : (False, 'specsegs'), -INF_VOIDS : (False, 's_void'), # char; Display void marks? -INF_SHOWAUTO : (False, 's_showauto'), # char; Display autoanalysis indicator? -INF_AUTO : (False, 's_auto'), # char; Autoanalysis is enabled? -INF_BORDER : (False, 's_limiter'), # char; Generate borders? -INF_NULL : (False, 's_null'), # char; Generate empty lines? -INF_GENFLAGS : (False, 's_genflags'), # char; General flags: -INF_SHOWPREF : (False, 's_showpref'), # char; Show line prefixes? -INF_PREFSEG : (False, 's_prefseg'), # char; line prefixes with segment name? -INF_ASMTYPE : (False, 'asmtype'), # char; target assembler number (0..n) -INF_BASEADDR : (False, 'baseaddr'), # long; base paragraph of the program -INF_XREFS : (False, 's_xrefflag'), # char; xrefs representation: -INF_BINPREF : (False, 'binSize'), # short; # of instruction bytes to show -INF_CMTFLAG : (False, 's_cmtflg'), # char; comments: -INF_NAMETYPE : (False, 'nametype'), # char; dummy names represenation type -INF_SHOWBADS : (False, 's_showbads'), # char; show bad instructions? -INF_PREFFLAG : (False, 's_prefflag'), # char; line prefix type: -INF_PACKBASE : (False, 's_packbase'), # char; pack database? -INF_ASCIIFLAGS : (False, 'asciiflags'), # uchar; ascii flags -INF_LISTNAMES : (False, 'listnames'), # uchar; What names should be included in the list? -INF_ASCIIPREF : (False, 'ASCIIpref'), # char[16];ASCII names prefix -INF_ASCIISERNUM : (False, 'ASCIIsernum'), # ulong; serial number -INF_ASCIIZEROES : (False, 'ASCIIzeroes'), # char; leading zeroes -INF_MF : (False, 'mf'), # uchar; Byte order: 1==MSB first -INF_ORG : (False, 's_org'), # char; Generate 'org' directives? -INF_ASSUME : (False, 's_assume'), # char; Generate 'assume' directives? -INF_CHECKARG : (False, 's_checkarg'), # char; Check manual operands? -INF_START_SS : (False, 'start_ss'), # long; value of SS at the start -INF_START_CS : (False, 'start_cs'), # long; value of CS at the start -INF_MAIN : (False, 'main'), # long; address of main() -INF_SHORT_DN : (False, 'short_demnames'), # long; short form of demangled names -INF_LONG_DN : (False, 'long_demnames'), # long; long form of demangled names -INF_DATATYPES : (False, 'datatypes'), # long; data types allowed in data carousel -INF_STRTYPE : (False, 'strtype'), # long; current ascii string type -INF_AF2 : (False, 'af2'), # ushort; Analysis flags 2 -INF_NAMELEN : (False, 'namelen'), # ushort; max name length (without zero byte) -INF_MARGIN : (False, 'margin'), # ushort; max length of data lines -INF_LENXREF : (False, 'lenxref'), # ushort; max length of line with xrefs -INF_LPREFIX : (False, 'lprefix'), # char[16];prefix of local names -INF_LPREFIXLEN : (False, 'lprefixlen'), # uchar; length of the lprefix -INF_COMPILER : (False, 'cc') # uchar; compiler - -#INF_MODEL = 184 # uchar; memory model & calling convention -#INF_SIZEOF_INT = 185 # uchar; sizeof(int) -#INF_SIZEOF_BOOL = 186 # uchar; sizeof(bool) -#INF_SIZEOF_ENUM = 187 # uchar; sizeof(enum) -#INF_SIZEOF_ALGN = 188 # uchar; default alignment -#INF_SIZEOF_SHORT = 189 -#INF_SIZEOF_LONG = 190 -#INF_SIZEOF_LLONG = 191 -} - - -def SetProcessorType (processor, level): - """ - Change current processor - - @param processor: name of processor in short form. - run 'ida ?' to get list of allowed processor types - @param level: the power of request: - - SETPROC_COMPAT - search for the processor type in the current module - - SETPROC_ALL - search for the processor type in all modules - only if there were not calls with SETPROC_USER - - SETPROC_USER - search for the processor type in all modules - and prohibit level SETPROC_USER - - SETPROC_FATAL - can be combined with previous bits. - means that if the processor type can't be - set, IDA should display an error message and exit. - """ - return idaapi.set_processor_type(processor, level) - -SETPROC_COMPAT = idaapi.SETPROC_COMPAT -SETPROC_ALL = idaapi.SETPROC_ALL -SETPROC_USER = idaapi.SETPROC_USER -SETPROC_FATAL = idaapi.SETPROC_FATAL - -def SetPrcsr(processor): return SetProcessorType(processor, SETPROC_COMPAT) - - -def Batch(batch): - """ - Enable/disable batch mode of operation - - @param batch: Batch mode - 0 - ida will display dialog boxes and wait for the user input - 1 - ida will not display dialog boxes, warnings, etc. - - @return: old balue of batch flag - """ - batch_prev = idaapi.cvar.batch - idaapi.cvar.batch = batch - return batch_prev - - -#---------------------------------------------------------------------------- -# I N T E R A C T I O N W I T H T H E U S E R -#---------------------------------------------------------------------------- -def AskStr(defval, prompt): - """ - Ask the user to enter a string - - @param defval: the default string value. This value will appear - in the dialog box. - @param prompt: the prompt to display in the dialog box - - @return: the entered string or 0. - """ - return idaapi.askstr(0, defval, prompt) - - -def AskFile(forsave, mask, prompt): - """ - Ask the user to choose a file - - @param forsave: 0: "Open" dialog box, 1: "Save" dialog box - @param mask: the input file mask as "*.*" or the default file name. - @param prompt: the prompt to display in the dialog box - - @return: the selected file or 0. - """ - return idaapi.askfile_c(forsave, mask, prompt) - - -def AskAddr(defval, prompt): - """ - Ask the user to enter an address - - @param defval: the default address value. This value - will appear in the dialog box. - @param prompt: the prompt to display in the dialog box - - @return: the entered address or BADADDR. - """ - return idaapi.askaddr(defval, prompt) - - -def AskLong(defval, prompt): - """ - Ask the user to enter a number - - @param defval: the default value. This value - will appear in the dialog box. - @param prompt: the prompt to display in the dialog box - - @return: the entered number or -1. - """ - return idaapi.asklong(defval, prompt) - - -def AskSeg(defval, prompt): - """ - Ask the user to enter a segment value - - @param defval: the default value. This value - will appear in the dialog box. - @param prompt: the prompt to display in the dialog box - - @return: the entered segment selector or BADSEL. - """ - return idaapi.askseg(defval, prompt) - - -def AskIdent(defval, prompt): - """ - Ask the user to enter an identifier - - @param defval: the default identifier. This value will appear in - the dialog box. - @param prompt: the prompt to display in the dialog box - - @return: the entered identifier or 0. - """ - return idaapi.askident(defval, prompt) - - -def AskYN(defval, prompt): - """ - Ask the user a question and let him answer Yes/No/Cancel - - @param defval: the default answer. This answer will be selected if the user - presses Enter. -1:cancel,0-no,1-ok - @param prompt: the prompt to display in the dialog box - - @return: -1:cancel,0-no,1-ok - """ - return idaapi.askyn_c(defval, prompt) - - -def Message(msg): - """ - Display a message in the message window - - @param msg: message to print (formatting is done in Python) - - This function can be used to debug IDC scripts - """ - idaapi.msg(msg) - - -def Warning(msg): - """ - Display a message in a message box - - @param msg: message to print (formatting is done in Python) - - This function can be used to debug IDC scripts - The user will be able to hide messages if they appear twice in a row on - the screen - """ - idaapi.warning(msg) - - -def Fatal(format): - """ - Display a fatal message in a message box and quit IDA - - @param format: message to print - """ - idaapi.error(format) - - -def SetStatus(status): - """ - Change IDA indicator. - - @param status: new status - - @return: the previous status. - """ - return idaapi.setStat(status) - - -IDA_STATUS_READY = 0 # READY IDA is idle -IDA_STATUS_THINKING = 1 # THINKING Analyzing but the user may press keys -IDA_STATUS_WAITING = 2 # WAITING Waiting for the user input -IDA_STATUS_WORK = 3 # BUSY IDA is busy - - -def Refresh(): - """ - Refresh all disassembly views - """ - idaapi.refresh_idaview_anyway() - - -def RefreshLists(): - """ - Refresh all list views (names, functions, etc) - """ - idaapi.refresh_lists() - - -#---------------------------------------------------------------------------- -# S E G M E N T A T I O N -#---------------------------------------------------------------------------- -def AskSelector(sel): - """ - Get a selector value - - @param sel: the selector number - - @return: selector value if found - otherwise the input value (sel) - - @note: selector values are always in paragraphs - """ - s = idaapi.sel_pointer() - base = idaapi.ea_pointer() - res,tmp = idaapi.getn_selector(sel, s.cast(), base.cast()) - - if not res: - return sel - else: - return base.value() - - -def FindSelector(val): - """ - Find a selector which has the specifed value - - @param val: value to search for - - @return: the selector number if found, - otherwise the input value (val & 0xFFFF) - - @note: selector values are always in paragraphs - """ - return idaapi.find_selector(val) & 0xFFFF - - -def SetSelector(sel, value): - """ - Set a selector value - - @param sel: the selector number - @param value: value of selector - - @return: None - - @note: ida supports up to 4096 selectors. - if 'sel' == 'val' then the selector is destroyed because - it has no significance - """ - return idaapi.set_selector(sel, value) - - -def DelSelector(sel): - """ - Delete a selector - - @param sel: the selector number to delete - - @return: None - - @note: if the selector is found, it will be deleted - """ - return idaapi.del_selector(sel) - - -def FirstSeg(): - """ - Get first segment - - @return: address of the start of the first segment - BADADDR - no segments are defined - """ - seg = idaapi.get_first_seg() - if not seg: - return BADADDR - else: - return seg.startEA - - -def NextSeg(ea): - """ - Get next segment - - @param ea: linear address - - @return: start of the next segment - BADADDR - no next segment - """ - nextseg = idaapi.get_next_seg(ea) - if not nextseg: - return BADADDR - else: - return nextseg.startEA - - return BADADDR - - -def SegStart(ea): - """ - Get start address of a segment - - @param ea: any address in the segment - - @return: start of segment - BADADDR - the specified address doesn't belong to any segment - """ - seg = idaapi.getseg(ea) - - if not seg: - return BADADDR - else: - return seg.startEA - - -def SegEnd(ea): - """ - Get end address of a segment - - @param ea: any address in the segment - - @return: end of segment (an address past end of the segment) - BADADDR - the specified address doesn't belong to any segment - """ - seg = idaapi.getseg(ea) - - if not seg: - return BADADDR - else: - return seg.endEA - - -def SegName(ea): - """ - Get name of a segment - - @param ea: any address in the segment - - @return: "" - no segment at the specified address - """ - seg = idaapi.getseg(ea) - - if not seg: - return "" - else: - name = idaapi.get_true_segm_name(seg) - - if not name: - return "" - else: - return name - - -def AddSeg(startea, endea, base, use32, align, comb): - """ - Create a new segment - - @param startea: linear address of the start of the segment - @param endea: linear address of the end of the segment - this address will not belong to the segment - 'endea' should be higher than 'startea' - @param base: base paragraph or selector of the segment. - a paragraph is 16byte memory chunk. - If a selector value is specified, the selector should be - already defined. - @param use32: 0: 16bit segment, 1: 32bit segment, 2: 64bit segment - @param align: segment alignment. see below for alignment values - @param comb: segment combination. see below for combination values. - - @return: 0-failed, 1-ok - """ - s = idaapi.segment_t() - s.startEA = startea - s.endEA = endea - s.sel = idaapi.setup_selector(base) - s.bitness = use32 - s.align = align - s.comb = comb - return idaapi.add_segm_ex(s, "", "", idaapi.ADDSEG_NOSREG) - - -def DelSeg(ea, flags): - """ - Delete a segment - - @param ea: any address in the segment - @param flags: combination of SEGMOD_* flags - - @return: boolean success - """ - return idaapi.del_segm(ea, flags) - -SEGMOD_KILL = idaapi.SEGMOD_KILL # disable addresses if segment gets - # shrinked or deleted -SEGMOD_KEEP = idaapi.SEGMOD_KEEP # keep information (code & data, etc) -SEGMOD_SILENT = idaapi.SEGMOD_SILENT # be silent - - -def SetSegBounds(ea, startea, endea, flags): - """ - Change segment boundaries - - @param ea: any address in the segment - @param startea: new start address of the segment - @param endea: new end address of the segment - @param flags: combination of SEGMOD_... flags - - @return: boolean success - """ - return idaapi.set_segm_start(ea, startea, flags) & \ - idaapi.set_segm_end(ea, endea, flags) - - -def RenameSeg(ea, name): - """ - Change name of the segment - - @param ea: any address in the segment - @param name: new name of the segment - - @return: success (boolean) - """ - seg = idaapi.getseg(ea) - - if not seg: - return False - - return idaapi.set_segm_name(seg, name) - - -def SetSegClass(ea, segclass): - """ - Change class of the segment - - @param ea: any address in the segment - @param segclass: new class of the segment - - @return: success (boolean) - """ - seg = idaapi.getseg(ea) - - if not seg: - return False - - return idaapi.set_segm_class(seg, segclass) - - -def SegAlign(ea, alignment): - """ - Change alignment of the segment - - @param ea: any address in the segment - @param alignment: new alignment of the segment (one of the sa... constants) - - @return: success (boolean) - """ - return SetSegmentAttr(ea, SEGATTR_ALIGN, alignment) - - -saAbs = idaapi.saAbs # Absolute segment. -saRelByte = idaapi.saRelByte # Relocatable, byte aligned. -saRelWord = idaapi.saRelWord # Relocatable, word (2-byte, 16-bit) aligned. -saRelPara = idaapi.saRelPara # Relocatable, paragraph (16-byte) aligned. -saRelPage = idaapi.saRelPage # Relocatable, aligned on 256-byte boundary - # (a "page" in the original Intel specification). -saRelDble = idaapi.saRelDble # Relocatable, aligned on a double word - # (4-byte) boundary. This value is used by - # the PharLap OMF for the same alignment. -saRel4K = idaapi.saRel4K # This value is used by the PharLap OMF for - # page (4K) alignment. It is not supported - # by LINK. -saGroup = idaapi.saGroup # Segment group -saRel32Bytes = idaapi.saRel32Bytes # 32 bytes -saRel64Bytes = idaapi.saRel64Bytes # 64 bytes -saRelQword = idaapi.saRelQword # 8 bytes - - -def SegComb(segea, comb): - """ - Change combination of the segment - - @param segea: any address in the segment - @param comb: new combination of the segment (one of the sc... constants) - - @return: success (boolean) - """ - return SetSegmentAttr(segea, SEGATTR_COMB, comb) - - -scPriv = idaapi.scPriv # Private. Do not combine with any other program - # segment. -scPub = idaapi.scPub # Public. Combine by appending at an offset that - # meets the alignment requirement. -scPub2 = idaapi.scPub2 # As defined by Microsoft, same as C=2 (public). -scStack = idaapi.scStack # Stack. Combine as for C=2. This combine type - # forces byte alignment. -scCommon = idaapi.scCommon # Common. Combine by overlay using maximum size. -scPub3 = idaapi.scPub3 # As defined by Microsoft, same as C=2 (public). - - -def SetSegAddressing(ea, bitness): - """ - Change segment addressing - - @param ea: any address in the segment - @param bitness: 0: 16bit, 1: 32bit, 2: 64bit - - @return: success (boolean) - """ - seg = idaapi.getseg(ea) - - if not seg: - return False - - seg.bitness = bitness - - return True - - -def SegByName(segname): - """ - Get segment by name - - @param segname: name of segment - - @return: segment selector or BADADDR - """ - seg = idaapi.get_segm_by_name(segname) - - if not seg: - return BADADDR - - return seg.startEA - - -def SetSegDefReg(ea, reg, value): - """ - Set default segment register value for a segment - - @param ea: any address in the segment - if no segment is present at the specified address - then all segments will be affected - @param reg: name of segment register - @param value: default value of the segment register. -1-undefined. - """ - seg = idaapi.getseg(ea) - - reg = idaapi.str2reg(reg); - if seg and reg >= 0: - return idaapi.SetDefaultRegisterValue(seg, reg, value) - else: - return False - - -def SetSegmentType(segea, segtype): - """ - Set segment type - - @param segea: any address within segment - @param segtype: new segment type: - - @return: !=0 - ok - """ - seg = idaapi.getseg(segea) - - if not seg: - return False - - seg.type = segtype - return seg.update() - - -SEG_NORM = idaapi.SEG_NORM -SEG_XTRN = idaapi.SEG_XTRN # * segment with 'extern' definitions - # no instructions are allowed -SEG_CODE = idaapi.SEG_CODE # pure code segment -SEG_DATA = idaapi.SEG_DATA # pure data segment -SEG_IMP = idaapi.SEG_IMP # implementation segment -SEG_GRP = idaapi.SEG_GRP # * group of segments - # no instructions are allowed -SEG_NULL = idaapi.SEG_NULL # zero-length segment -SEG_UNDF = idaapi.SEG_UNDF # undefined segment type -SEG_BSS = idaapi.SEG_BSS # uninitialized segment -SEG_ABSSYM = idaapi.SEG_ABSSYM # * segment with definitions of absolute symbols - # no instructions are allowed -SEG_COMM = idaapi.SEG_COMM # * segment with communal definitions - # no instructions are allowed -SEG_IMEM = idaapi.SEG_IMEM # internal processor memory & sfr (8051) - - -def GetSegmentAttr(segea, attr): - """ - Get segment attribute - - @param segea: any address within segment - @param attr: one of SEGATTR_... constants - """ - seg = idaapi.getseg(segea) - assert seg, "could not find segment at 0x%x" % segea - if attr in [ SEGATTR_ES, SEGATTR_CS, SEGATTR_SS, SEGATTR_DS, SEGATTR_FS, SEGATTR_GS ]: - return idaapi.get_defsr(seg, _SEGATTRMAP[attr]) - else: - return _IDC_GetAttr(seg, _SEGATTRMAP, attr) - - -def SetSegmentAttr(segea, attr, value): - """ - Set segment attribute - - @param segea: any address within segment - @param attr: one of SEGATTR_... constants - - @note: Please note that not all segment attributes are modifiable. - Also some of them should be modified using special functions - like SetSegAddressing, etc. - """ - seg = idaapi.getseg(segea) - assert seg, "could not find segment at 0x%x" % segea - if attr in [ SEGATTR_ES, SEGATTR_CS, SEGATTR_SS, SEGATTR_DS, SEGATTR_FS, SEGATTR_GS ]: - idaapi.set_defsr(seg, _SEGATTRMAP[attr], value) - else: - _IDC_SetAttr(seg, _SEGATTRMAP, attr, value) - return seg.update() - - -SEGATTR_START = 0 # starting address -SEGATTR_END = 4 # ending address -SEGATTR_ORGBASE = 16 -SEGATTR_ALIGN = 20 # alignment -SEGATTR_COMB = 21 # combination -SEGATTR_PERM = 22 # permissions -SEGATTR_BITNESS = 23 # bitness (0: 16, 1: 32, 2: 64 bit segment) - # Note: modifying the attribute directly does - # not lead to the reanalysis of the segment. - # Using SetSegAddressing() is more correct. -SEGATTR_FLAGS = 24 # segment flags -SEGATTR_SEL = 26 # segment selector -SEGATTR_ES = 30 # default ES value -SEGATTR_CS = 34 # default CS value -SEGATTR_SS = 38 # default SS value -SEGATTR_DS = 42 # default DS value -SEGATTR_FS = 46 # default FS value -SEGATTR_GS = 50 # default GS value -SEGATTR_TYPE = 94 # segment type -SEGATTR_COLOR = 95 # segment color - -# Redefining these for 64-bit -if __EA64__: - SEGATTR_START = 0 - SEGATTR_END = 8 - SEGATTR_ORGBASE = 32 - SEGATTR_ALIGN = 40 - SEGATTR_COMB = 41 - SEGATTR_PERM = 42 - SEGATTR_BITNESS = 43 - SEGATTR_FLAGS = 44 - SEGATTR_SEL = 46 - SEGATTR_ES = 54 - SEGATTR_CS = 62 - SEGATTR_SS = 70 - SEGATTR_DS = 78 - SEGATTR_FS = 86 - SEGATTR_GS = 94 - SEGATTR_TYPE = 182 - SEGATTR_COLOR = 183 - -_SEGATTRMAP = { - SEGATTR_START : (True, 'startEA'), - SEGATTR_END : (True, 'endEA'), - SEGATTR_ORGBASE : (False, 'orgbase'), - SEGATTR_ALIGN : (False, 'align'), - SEGATTR_COMB : (False, 'comb'), - SEGATTR_PERM : (False, 'perm'), - SEGATTR_BITNESS : (False, 'bitness'), - SEGATTR_FLAGS : (False, 'flags'), - SEGATTR_SEL : (False, 'sel'), - SEGATTR_ES : (False, 0), - SEGATTR_CS : (False, 1), - SEGATTR_SS : (False, 2), - SEGATTR_DS : (False, 3), - SEGATTR_FS : (False, 4), - SEGATTR_GS : (False, 5), - SEGATTR_TYPE : (False, 'type'), - SEGATTR_COLOR : (False, 'color'), -} - -# Valid segment flags -SFL_COMORG = 0x01 # IDP dependent field (IBM PC: if set, ORG directive is not commented out) -SFL_OBOK = 0x02 # orgbase is present? (IDP dependent field) -SFL_HIDDEN = 0x04 # is the segment hidden? -SFL_DEBUG = 0x08 # is the segment created for the debugger? -SFL_LOADER = 0x10 # is the segment created by the loader? -SFL_HIDETYPE = 0x20 # hide segment type (do not print it in the listing) - - -def MoveSegm(ea, to, flags): - """ - Move a segment to a new address - This function moves all information to the new address - It fixes up address sensitive information in the kernel - The total effect is equal to reloading the segment to the target address - - @param ea: any address within the segment to move - @param to: new segment start address - @param flags: combination MFS_... constants - - @returns: MOVE_SEGM_... error code - """ - seg = idaapi.getseg(ea) - if not seg: - return MOVE_SEGM_PARAM - return idaapi.move_segm(seg, to, flags) - - -MSF_SILENT = 0x0001 # don't display a "please wait" box on the screen -MSF_NOFIX = 0x0002 # don't call the loader to fix relocations -MSF_LDKEEP = 0x0004 # keep the loader in the memory (optimization) -MSF_FIXONCE = 0x0008 # valid for rebase_program(): call loader only once - -MOVE_SEGM_OK = 0 # all ok -MOVE_SEGM_PARAM = -1 # The specified segment does not exist -MOVE_SEGM_ROOM = -2 # Not enough free room at the target address -MOVE_SEGM_IDP = -3 # IDP module forbids moving the segment -MOVE_SEGM_CHUNK = -4 # Too many chunks are defined, can't move -MOVE_SEGM_LOADER = -5 # The segment has been moved but the loader complained -MOVE_SEGM_ODD = -6 # Can't move segments by an odd number of bytes - - -def rebase_program(delta, flags): - """ - Rebase the whole program by 'delta' bytes - - @param delta: number of bytes to move the program - @param flags: combination of MFS_... constants - it is recommended to use MSF_FIXONCE so that the loader takes - care of global variables it stored in the database - - @returns: error code MOVE_SEGM_... - """ - return idaapi.rebase_program(delta, flags) - - -def SetStorageType(startEA, endEA, stt): - """ - Set storage type - - @param startEA: starting address - @param endEA: ending address - @param stt: new storage type, one of STT_VA and STT_MM - - @returns: 0 - ok, otherwise internal error code - """ - return idaapi.change_storage_type(startEA, endEA, stt) - - -STT_VA = 0 # regular storage: virtual arrays, an explicit flag for each byte -STT_MM = 1 # memory map: sparse storage. useful for huge objects - - -#---------------------------------------------------------------------------- -# C R O S S R E F E R E N C E S -#---------------------------------------------------------------------------- -# Flow types (combine with XREF_USER!): -fl_CF = 16 # Call Far -fl_CN = 17 # Call Near -fl_JF = 18 # Jump Far -fl_JN = 19 # Jump Near -fl_F = 21 # Ordinary flow - -XREF_USER = 32 # All user-specified xref types - # must be combined with this bit - - -# Mark exec flow 'from' 'to' -def AddCodeXref(From, To, flowtype): - """ - """ - return idaapi.add_cref(From, To, flowtype) - - -def DelCodeXref(From, To, undef): - """ - Unmark exec flow 'from' 'to' - - @param undef: make 'To' undefined if no more references to it - - @returns: 1 - planned to be made undefined - """ - return idaapi.del_cref(From, To, undef) - - -# The following functions include the ordinary flows: -# (the ordinary flow references are returned first) -def Rfirst(From): - """ - Get first code xref from 'From' - """ - return idaapi.get_first_cref_from(From) - - -def Rnext(From, current): - """ - Get next code xref from - """ - return idaapi.get_next_cref_from(From, current) - - -def RfirstB(To): - """ - Get first code xref to 'To' - """ - return idaapi.get_first_cref_to(To) - - -def RnextB(To, current): - """ - Get next code xref to 'To' - """ - return idaapi.get_next_cref_to(To, current) - - -# The following functions don't take into account the ordinary flows: -def Rfirst0(From): - """ - Get first xref from 'From' - """ - return idaapi.get_first_fcref_from(From) - - -def Rnext0(From, current): - """ - Get next xref from - """ - return idaapi.get_next_fcref_from(From, current) - - -def RfirstB0(To): - """ - Get first xref to 'To' - """ - return idaapi.get_first_fcref_to(To) - - -def RnextB0(To, current): - """ - Get next xref to 'To' - """ - return idaapi.get_next_fcref_to(To, current) - - -# Data reference types (combine with XREF_USER!): -dr_O = idaapi.dr_O # Offset -dr_W = idaapi.dr_W # Write -dr_R = idaapi.dr_R # Read -dr_T = idaapi.dr_T # Text (names in manual operands) -dr_I = idaapi.dr_I # Informational - - -def add_dref(From, To, drefType): - """ - Create Data Ref - """ - return idaapi.add_dref(From, To, drefType) - - -def del_dref(From, To): - """ - Unmark Data Ref - """ - return idaapi.del_dref(From, To) - - -def Dfirst(From): - """ - Get first data xref from 'From' - """ - return idaapi.get_first_dref_from(From) - - -def Dnext(From, current): - """ - Get next data xref from 'From' - """ - return idaapi.get_next_dref_from(From, current) - - -def DfirstB(To): - """ - Get first data xref to 'To' - """ - return idaapi.get_first_dref_to(To) - - -def DnextB(To, current): - """ - Get next data xref to 'To' - """ - return idaapi.get_next_dref_to(To, current) - - -def XrefType(): - """ - Return type of the last xref obtained by - [RD]first/next[B0] functions. - - @return: constants fl_* or dr_* - """ - raise DeprecatedIDCError, "use XrefsFrom() XrefsTo() from idautils instead." - - -#---------------------------------------------------------------------------- -# F I L E I / O -#---------------------------------------------------------------------------- -def fopen(f, mode): - raise DeprecatedIDCError, "fopen() deprecated. Use Python file objects instead." - -def fclose(handle): - raise DeprecatedIDCError, "fclose() deprecated. Use Python file objects instead." - -def filelength(handle): - raise DeprecatedIDCError, "filelength() deprecated. Use Python file objects instead." - -def fseek(handle, offset, origin): - raise DeprecatedIDCError, "fseek() deprecated. Use Python file objects instead." - -def ftell(handle): - raise DeprecatedIDCError, "ftell() deprecated. Use Python file objects instead." - - -def LoadFile(filepath, pos, ea, size): - """ - Load file into IDA database - - @param filepath: path to input file - @param pos: position in the file - @param ea: linear address to load - @param size: number of bytes to load - - @return: 0 - error, 1 - ok - """ - li = idaapi.open_linput(filepath, False) - - if li: - retval = idaapi.file2base(li, pos, ea, ea+size, False) - idaapi.close_linput(li) - return retval - else: - return 0 - -def loadfile(filepath, pos, ea, size): return LoadFile(filepath, pos, ea, size) - - -def SaveFile(filepath, pos, ea, size): - """ - Save from IDA database to file - - @param filepath: path to output file - @param pos: position in the file - @param ea: linear address to save from - @param size: number of bytes to save - - @return: 0 - error, 1 - ok - """ - of = idaapi.fopenWB(filepath) - - if of: - retval = idaapi.base2file(of, pos, ea, ea+size) - idaapi.eclose(of) - return retval - else: - return 0 - -def savefile(filepath, pos, ea, size): return SaveFile(filepath, pos, ea, size) - - -def fgetc(handle): - raise DeprecatedIDCError, "fgetc() deprecated. Use Python file objects instead." - -def fputc(byte, handle): - raise DeprecatedIDCError, "fputc() deprecated. Use Python file objects instead." - -def fprintf(handle, format, *args): - raise DeprecatedIDCError, "fprintf() deprecated. Use Python file objects instead." - -def readshort(handle, mostfirst): - raise DeprecatedIDCError, "readshort() deprecated. Use Python file objects instead." - -def readlong(handle, mostfirst): - raise DeprecatedIDCError, "readlong() deprecated. Use Python file objects instead." - -def writeshort(handle, word, mostfirst): - raise DeprecatedIDCError, "writeshort() deprecated. Use Python file objects instead." - -def writelong(handle, dword, mostfirst): - raise DeprecatedIDCError, "writelong() deprecated. Use Python file objects instead." - -def readstr(handle): - raise DeprecatedIDCError, "readstr() deprecated. Use Python file objects instead." - -def writestr(handle, s): - raise DeprecatedIDCError, "writestr() deprecated. Use Python file objects instead." - -# ---------------------------------------------------------------------------- -# F U N C T I O N S -# ---------------------------------------------------------------------------- - -def MakeFunction(start, end): - """ - Create a function - - @param start: function bounds - @param end: function bounds - - If the function end address is BADADDR, then - IDA will try to determine the function bounds - automatically. IDA will define all necessary - instructions to determine the function bounds. - - @return: !=0 - ok - - @note: an instruction should be present at the start address - """ - return idaapi.add_func(start, end) - - -def DelFunction(ea): - """ - Delete a function - - @param ea: any address belonging to the function - - @return: !=0 - ok - """ - return idaapi.del_func(ea) - - -def SetFunctionEnd(ea, end): - """ - Change function end address - - @param ea: any address belonging to the function - @param end: new function end address - - @return: !=0 - ok - """ - return idaapi.func_setend(ea, end) - - -def NextFunction(ea): - """ - Find next function - - @param ea: any address belonging to the function - - @return: -1 - no more functions - otherwise returns the next function start address - """ - func = idaapi.get_next_func(ea) - - if not func: - return BADADDR - else: - return func.startEA - - -def PrevFunction(ea): - """ - Find previous function - - @param ea: any address belonging to the function - - @return: -1 - no more functions - otherwise returns the previous function start address - """ - func = idaapi.get_prev_func(ea) - - if not func: - return BADADDR - else: - return func.startEA - - -def GetFunctionAttr(ea, attr): - """ - Get a function attribute - - @param ea: any address belonging to the function - @param attr: one of FUNCATTR_... constants - - @return: -1 - error otherwise returns the attribute value - """ - func = idaapi.get_func(ea) - - if func: - return _IDC_GetAttr(func, _FUNCATTRMAP, attr) - return BADADDR - - -def SetFunctionAttr(ea, attr, value): - """ - Set a function attribute - - @param ea: any address belonging to the function - @param attr: one of FUNCATTR_... constants - @param value: new value of the attribute - - @return: 1-ok, 0-failed - """ - func = idaapi.get_func(ea) - - if func: - _IDC_SetAttr(func, _FUNCATTRMAP, attr, value) - return idaapi.update_func(func) - return 0 - - -FUNCATTR_START = 0 # function start address -FUNCATTR_END = 4 # function end address -FUNCATTR_FLAGS = 8 # function flags -FUNCATTR_FRAME = 10 # function frame id -FUNCATTR_FRSIZE = 14 # size of local variables -FUNCATTR_FRREGS = 18 # size of saved registers area -FUNCATTR_ARGSIZE = 20 # number of bytes purged from the stack -FUNCATTR_FPD = 24 # frame pointer delta -FUNCATTR_COLOR = 28 # function color code -FUNCATTR_OWNER = 10 # chunk owner (valid only for tail chunks) -FUNCATTR_REFQTY = 14 # number of chunk parents (valid only for tail chunks) - -# Redefining the constants for 64-bit -if __EA64__: - FUNCATTR_START = 0 - FUNCATTR_END = 8 - FUNCATTR_FLAGS = 16 - FUNCATTR_FRAME = 18 - FUNCATTR_FRSIZE = 26 - FUNCATTR_FRREGS = 34 - FUNCATTR_ARGSIZE = 36 - FUNCATTR_FPD = 44 - FUNCATTR_COLOR = 52 - FUNCATTR_OWNER = 18 - FUNCATTR_REFQTY = 26 - - -_FUNCATTRMAP = { - FUNCATTR_START : (True, 'startEA'), - FUNCATTR_END : (True, 'endEA'), - FUNCATTR_FLAGS : (False, 'flags'), - FUNCATTR_FRAME : (True, 'frame'), - FUNCATTR_FRSIZE : (True, 'frsize'), - FUNCATTR_FRREGS : (True, 'frregs'), - FUNCATTR_ARGSIZE : (True, 'argsize'), - FUNCATTR_FPD : (False, 'fpd'), - FUNCATTR_COLOR : (False, 'color'), - FUNCATTR_OWNER : (True, 'owner'), - FUNCATTR_REFQTY : (True, 'refqty') -} - - -def GetFunctionFlags(ea): - """ - Retrieve function flags - - @param ea: any address belonging to the function - - @return: -1 - function doesn't exist otherwise returns the flags - """ - func = idaapi.get_func(ea) - - if not func: - return -1 - else: - return func.flags - - -FUNC_NORET = idaapi.FUNC_NORET # function doesn't return -FUNC_FAR = idaapi.FUNC_FAR # far function -FUNC_LIB = idaapi.FUNC_LIB # library function -FUNC_STATIC = idaapi.FUNC_STATIC # static function -FUNC_FRAME = idaapi.FUNC_FRAME # function uses frame pointer (BP) -FUNC_USERFAR = idaapi.FUNC_USERFAR # user has specified far-ness - # of the function -FUNC_HIDDEN = idaapi.FUNC_HIDDEN # a hidden function -FUNC_THUNK = idaapi.FUNC_THUNK # thunk (jump) function -FUNC_BOTTOMBP = idaapi.FUNC_BOTTOMBP # BP points to the bottom of the stack frame - - -def SetFunctionFlags(ea, flags): - """ - Change function flags - - @param ea: any address belonging to the function - @param flags: see GetFunctionFlags() for explanations - - @return: !=0 - ok - """ - func = idaapi.get_func(ea) - - if not func: - return 0 - else: - func.flags = flags - idaapi.update_func(func) - return 1 - - -def GetFunctionName(ea): - """ - Retrieve function name - - @param ea: any address belonging to the function - - @return: null string - function doesn't exist - otherwise returns function name - """ - name = idaapi.get_func_name(ea) - - if not name: - return "" - else: - return name - - -def GetFunctionCmt(ea, repeatable): - """ - Retrieve function comment - - @param ea: any address belonging to the function - @param repeatable: 1: get repeatable comment - 0: get regular comment - - @return: function comment string - """ - func = idaapi.get_func(ea) - - if not func: - return "" - else: - comment = idaapi.get_func_cmt(func, repeatable) - - if not comment: - return "" - else: - return comment - - -def SetFunctionCmt(ea, cmt, repeatable): - """ - Set function comment - - @param ea: any address belonging to the function - @param cmt: a function comment line - @param repeatable: 1: get repeatable comment - 0: get regular comment - """ - func = idaapi.get_func(ea) - - if not func: - return None - else: - return idaapi.set_func_cmt(func, cmt, repeatable) - - -def ChooseFunction(title): - """ - Ask the user to select a function - - Arguments: - - @param title: title of the dialog box - - @return: -1 - user refused to select a function - otherwise returns the selected function start address - """ - return idaapi.choose_func(title) - - -def GetFuncOffset(ea): - """ - Convert address to 'funcname+offset' string - - @param ea: address to convert - - @return: if the address belongs to a function then return a string - formed as 'name+offset' where 'name' is a function name - 'offset' is offset within the function else return null string - """ - return idaapi.a2funcoff(ea) - - -def FindFuncEnd(ea): - """ - Determine a new function boundaries - - @param ea: starting address of a new function - - @return: if a function already exists, then return its end address. - If a function end cannot be determined, the return BADADDR - otherwise return the end address of the new function - """ - func = idaapi.func_t() - - res = idaapi.find_func_bounds(ea, func, idaapi.FIND_FUNC_DEFINE) - - if res == idaapi.FIND_FUNC_UNDEF: - return BADADDR - else: - return func.endEA - - -def GetFrame(ea): - """ - Get ID of function frame structure - - @param ea: any address belonging to the function - - @return: ID of function frame or None In order to access stack variables - you need to use structure member manipulaion functions with the - obtained ID. - """ - frame = idaapi.get_frame(ea) - - if frame: - return frame.id - else: - return None - - -def GetFrameLvarSize(ea): - """ - Get size of local variables in function frame - - @param ea: any address belonging to the function - - @return: Size of local variables in bytes. - If the function doesn't have a frame, return 0 - If the function does't exist, return None - """ - return GetFunctionAttr(ea, FUNCATTR_FRSIZE) - - -def GetFrameRegsSize(ea): - """ - Get size of saved registers in function frame - - @param ea: any address belonging to the function - - @return: Size of saved registers in bytes. - If the function doesn't have a frame, return 0 - This value is used as offset for BP (if FUNC_FRAME is set) - If the function does't exist, return None - """ - return GetFunctionAttr(ea, FUNCATTR_FRREGS) - - -def GetFrameArgsSize(ea): - """ - Get size of arguments in function frame which are purged upon return - - @param ea: any address belonging to the function - - @return: Size of function arguments in bytes. - If the function doesn't have a frame, return 0 - If the function does't exist, return -1 - """ - return GetFunctionAttr(ea, FUNCATTR_ARGSIZE) - - -def GetFrameSize(ea): - """ - Get full size of function frame - - @param ea: any address belonging to the function - @returns: Size of function frame in bytes. - This function takes into account size of local - variables + size of saved registers + size of - return address + size of function arguments - If the function doesn't have a frame, return size of - function return address in the stack. - If the function does't exist, return 0 - """ - func = idaapi.get_func(ea) - - if not func: - return 0 - else: - return idaapi.get_frame_size(func) - - -def MakeFrame(ea, lvsize, frregs, argsize): - """ - Make function frame - - @param ea: any address belonging to the function - @param lvsize: size of function local variables - @param frregs: size of saved registers - @param argsize: size of function arguments - - @return: ID of function frame or -1 - If the function did not have a frame, the frame - will be created. Otherwise the frame will be modified - """ - func = idaapi.get_func(ea) - - if not func: - return -1 - - frameid = idaapi.add_frame(func, lvsize, frregs, argsize) - - if not frameid: - if not idaapi.set_frame_size(func, lvsize, frregs, argsize): - return -1 - - return func.frame - - -def GetSpd(ea): - """ - Get current delta for the stack pointer - - @param ea: end address of the instruction - i.e.the last address of the instruction+1 - - @return: The difference between the original SP upon - entering the function and SP for the specified address - """ - func = idaapi.get_func(ea) - - if not func: - return None - - return idaapi.get_spd(func, ea) - - -def GetSpDiff(ea): - """ - Get modification of SP made by the instruction - - @param ea: end address of the instruction - i.e.the last address of the instruction+1 - - @return: Get modification of SP made at the specified location - If the specified location doesn't contain a SP change point, return 0 - Otherwise return delta of SP modification - """ - func = idaapi.get_func(ea) - - if not func: - return None - - return idaapi.get_sp_delta(func, ea) - - -def SetSpDiff(ea, delta): - """ - Setup modification of SP made by the instruction - - @param ea: end address of the instruction - i.e.the last address of the instruction+1 - @param delta: the difference made by the current instruction. - - @return: 1-ok, 0-failed - """ - return idaapi.add_user_stkpnt(ea, delta) - - -# ---------------------------------------------------------------------------- -# E N T R Y P O I N T S -# ---------------------------------------------------------------------------- - -def GetEntryPointQty(): - """ - Retrieve number of entry points - - @returns: number of entry points - """ - return idaapi.get_entry_qty() - - -def AddEntryPoint(ordinal, ea, name, makecode): - """ - Add entry point - - @param ordinal: entry point number - if entry point doesn't have an ordinal - number, 'ordinal' should be equal to 'ea' - @param ea: address of the entry point - @param name: name of the entry point. If null string, - the entry point won't be renamed. - @param makecode: if 1 then this entry point is a start - of a function. Otherwise it denotes data bytes. - - @return: 0 - entry point with the specifed ordinal already exists - 1 - ok - """ - return idaapi.add_entry(ordinal, ea, name, makecode) - - -def GetEntryOrdinal(index): - """ - Retrieve entry point ordinal number - - @param index: 0..GetEntryPointQty()-1 - - @return: 0 if entry point doesn't exist - otherwise entry point ordinal - """ - return idaapi.get_entry_ordinal(index) - - -def GetEntryPoint(ordinal): - """ - Retrieve entry point address - - @param ordinal: entry point number - it is returned by GetEntryPointOrdinal() - - @return: -1 if entry point doesn't exist - otherwise entry point address. - If entry point address is equal to its ordinal - number, then the entry point has no ordinal. - """ - return idaapi.get_entry(ordinal) - - -def GetEntryName(ordinal): - """ - Retrieve entry point name - - @param ordinal: entry point number, ass returned by GetEntryPointOrdinal() - - @return: entry point name or None - """ - return idaapi.get_entry_name(ordinal) - - -def RenameEntryPoint(ordinal, name): - """ - Rename entry point - - @param ordinal: entry point number - @param name: new name - - @return: !=0 - ok - """ - return idaapi.rename_entry(ordinal, name) - - -# ---------------------------------------------------------------------------- -# F I X U P S -# ---------------------------------------------------------------------------- -def GetNextFixupEA(ea): - """ - Find next address with fixup information - - @param ea: current address - - @return: -1 - no more fixups otherwise returns the next - address with fixup information - """ - return idaapi.get_next_fixup_ea(ea) - - -def GetPrevFixupEA(ea): - """ - Find previous address with fixup information - - @param ea: current address - - @return: -1 - no more fixups otherwise returns the - previous address with fixup information - """ - return idaapi.get_prev_fixup_ea(ea) - - -def GetFixupTgtType(ea): - """ - Get fixup target type - - @param ea: address to get information about - - @return: -1 - no fixup at the specified address - otherwise returns fixup target type: - """ - fd = idaapi.get_fixup(ea) - - if not fd: - return -1 - - return fd.type - - -FIXUP_MASK = 0xF -FIXUP_OFF8 = 0 # 8-bit offset. -FIXUP_BYTE = FIXUP_OFF8 # 8-bit offset. -FIXUP_OFF16 = 1 # 16-bit offset. -FIXUP_SEG16 = 2 # 16-bit base--logical segment base (selector). -FIXUP_PTR32 = 3 # 32-bit long pointer (16-bit base:16-bit - # offset). -FIXUP_OFF32 = 4 # 32-bit offset. -FIXUP_PTR48 = 5 # 48-bit pointer (16-bit base:32-bit offset). -FIXUP_HI8 = 6 # high 8 bits of 16bit offset -FIXUP_HI16 = 7 # high 16 bits of 32bit offset -FIXUP_LOW8 = 8 # low 8 bits of 16bit offset -FIXUP_LOW16 = 9 # low 16 bits of 32bit offset -FIXUP_REL = 0x10 # fixup is relative to the linear address - # specified in the 3d parameter to set_fixup() -FIXUP_SELFREL = 0x0 # self-relative? - # - disallows the kernel to convert operands - # in the first pass - # - this fixup is used during output - # This type of fixups is not used anymore. - # Anyway you can use it for commenting purposes - # in the loader modules -FIXUP_EXTDEF = 0x20 # target is a location (otherwise - segment) -FIXUP_UNUSED = 0x40 # fixup is ignored by IDA - # - disallows the kernel to convert operands - # - this fixup is not used during output -FIXUP_CREATED = 0x80 # fixup was not present in the input file - - -def GetFixupTgtSel(ea): - """ - Get fixup target selector - - @param ea: address to get information about - - @return: -1 - no fixup at the specified address - otherwise returns fixup target selector - """ - fd = idaapi.get_fixup(ea) - - if not fd: - return -1 - - return fd.sel - - -def GetFixupTgtOff(ea): - """ - Get fixup target offset - - @param ea: address to get information about - - @return: -1 - no fixup at the specified address - otherwise returns fixup target offset - """ - fd = idaapi.get_fixup(ea) - - if not fd: - return -1 - - return fd.off - - -def GetFixupTgtDispl(ea): - """ - Get fixup target displacement - - @param ea: address to get information about - - @return: -1 - no fixup at the specified address - otherwise returns fixup target displacement - """ - fd = idaapi.get_fixup(ea) - - if not fd: - return -1 - - return fd.displacement - - -def SetFixup(ea, fixuptype, targetsel, targetoff, displ): - """ - Set fixup information - - @param ea: address to set fixup information about - @param fixuptype: fixup type. see GetFixupTgtType() - for possible fixup types. - @param targetsel: target selector - @param targetoff: target offset - @param displ: displacement - - @return: none - """ - fd = idaapi.fixup_data_t() - fd.type = fixuptype - fd.sel = targetsel - fd.off = targetoff - fd.displacement = displ - - idaapi.set_fixup(ea, fd) - - -def DelFixup(ea): - """ - Delete fixup information - - @param ea: address to delete fixup information about - - @return: None - """ - idaapi.del_fixup(ea) - - -#---------------------------------------------------------------------------- -# M A R K E D P O S I T I O N S -#---------------------------------------------------------------------------- - -def MarkPosition(ea, lnnum, x, y, slot, comment): - """ - Mark position - - @param ea: address to mark - @param lnnum: number of generated line for the 'ea' - @param x: x coordinate of cursor - @param y: y coordinate of cursor - @param slot: slot number: 1..1024 - if the specifed value is not within the - range, IDA will ask the user to select slot. - @param comment: description of the mark. Should be not empty. - - @return: None - """ - curloc = idaapi.curloc() - curloc.ea = ea - curloc.lnnum = lnnum - curloc.x = x - curloc.y = y - curloc.mark(slot, comment, comment) - - -def GetMarkedPos(slot): - """ - Get marked position - - @param slot: slot number: 1..1024 if the specifed value is <= 0 - range, IDA will ask the user to select slot. - - @return: BADADDR - the slot doesn't contain a marked address - otherwise returns the marked address - """ - curloc = idaapi.curloc() - intp = idaapi.int_pointer() - intp.assign(slot) - return curloc.markedpos(intp) - - -def GetMarkComment(slot): - """ - Get marked position comment - - @param slot: slot number: 1..1024 - - @return: None if the slot doesn't contain a marked address - otherwise returns the marked address comment - """ - curloc = idaapi.curloc() - return curloc.markdesc(slot) - - -# ---------------------------------------------------------------------------- -# S T R U C T U R E S -# ---------------------------------------------------------------------------- - -def GetStrucQty(): - """ - Get number of defined structure types - - @return: number of structure types - """ - return idaapi.get_struc_qty() - - -def GetFirstStrucIdx(): - """ - Get index of first structure type - - @return: -1 if no structure type is defined - index of first structure type. - Each structure type has an index and ID. - INDEX determines position of structure definition - in the list of structure definitions. Index 1 - is listed first, after index 2 and so on. - The index of a structure type can be changed any - time, leading to movement of the structure definition - in the list of structure definitions. - ID uniquely denotes a structure type. A structure - gets a unique ID at the creation time and this ID - can't be changed. Even when the structure type gets - deleted, its ID won't be resued in the future. - """ - return idaapi.get_first_struc_idx() - - -def GetLastStrucIdx(): - """ - Get index of last structure type - - @return: -1 if no structure type is defined - index of last structure type. - See GetFirstStrucIdx() for the explanation of - structure indices and IDs. - """ - return idaapi.get_last_struc_idx() - - -def GetNextStrucIdx(index): - """ - Get index of next structure type - - @param index: current structure index - - @return: -1 if no (more) structure type is defined - index of the next structure type. - See GetFirstStrucIdx() for the explanation of - structure indices and IDs. - """ - return idaapi.get_next_struc_idx(index) - - -def GetPrevStrucIdx(index): - """ - Get index of previous structure type - - @param index: current structure index - - @return: -1 if no (more) structure type is defined - index of the presiouvs structure type. - See GetFirstStrucIdx() for the explanation of - structure indices and IDs. - """ - return idaapi.get_prev_struc_idx(index) - - -def GetStrucIdx(sid): - """ - Get structure index by structure ID - - @param sid: structure ID - - @return: -1 if bad structure ID is passed - otherwise returns structure index. - See GetFirstStrucIdx() for the explanation of - structure indices and IDs. - """ - return idaapi.get_struc_idx(sid) - - -def GetStrucId(index): - """ - Get structure ID by structure index - - @param index: structure index - - @return: -1 if bad structure index is passed otherwise returns structure ID. - - @note: See GetFirstStrucIdx() for the explanation of structure indices and IDs. - """ - return idaapi.get_struc_by_idx(index) - - -def GetStrucIdByName(name): - """ - Get structure ID by structure name - - @param name: structure type name - - @return: -1 if bad structure type name is passed - otherwise returns structure ID. - """ - return idaapi.get_struc_id(name) - - -def GetStrucName(sid): - """ - Get structure type name - - @param sid: structure type ID - - @return: -1 if bad structure type ID is passed - otherwise returns structure type name. - """ - return idaapi.get_struc_name(sid) - - -def GetStrucComment(sid, repeatable): - """ - Get structure type comment - - @param sid: structure type ID - @param repeatable: 1: get repeatable comment - 0: get regular comment - - @return: None if bad structure type ID is passed - otherwise returns comment. - """ - return idaapi.get_struc_cmt(sid, repeatable) - - -def GetStrucSize(sid): - """ - Get size of a structure - - @param sid: structure type ID - - @return: -1 if bad structure type ID is passed - otherwise returns size of structure in bytes. - """ - return idaapi.get_struc_size(sid) - - -def GetMemberQty(sid): - """ - Get number of members of a structure - - @param sid: structure type ID - - @return: -1 if bad structure type ID is passed otherwise - returns number of members. - """ - s = idaapi.get_struc(sid) - if not s: - return -1 - - return s.memqty - - -def GetStrucPrevOff(sid, offset): - """ - Get previous offset in a structure - - @param sid: structure type ID - @param offset: current offset - - @return: -1 if bad structure type ID is passed - or no (more) offsets in the structure - otherwise returns previous offset in a structure. - - @note: IDA allows 'holes' between members of a - structure. It treats these 'holes' - as unnamed arrays of bytes. - This function returns a member offset or a hole offset. - It will return size of the structure if input - 'offset' is bigger than the structure size. - """ - s = idaapi.get_struc(sid) - if not s: - return -1 - - return idaapi.get_struc_prev_offset(s, offset) - - -def GetStrucNextOff(sid, offset): - """ - Get next offset in a structure - - @param sid: structure type ID - @param offset: current offset - - @return: -1 if bad structure type ID is passed - or no (more) offsets in the structure - otherwise returns next offset in a structure. - - @note: IDA allows 'holes' between members of a - structure. It treats these 'holes' - as unnamed arrays of bytes. - This function returns a member offset or a hole offset. - It will return size of the structure if input - 'offset' belongs to the last member of the structure. - """ - s = idaapi.get_struc(sid) - if not s: - return -1 - - return idaapi.get_struc_next_offset(s, offset) - - -def GetFirstMember(sid): - """ - Get offset of the first member of a structure - - @param sid: structure type ID - - @return: -1 if bad structure type ID is passed - or structure has no members - otherwise returns offset of the first member. - - @note: IDA allows 'holes' between members of a - structure. It treats these 'holes' - as unnamed arrays of bytes. - """ - s = idaapi.get_struc(sid) - if not s: - return -1 - - return idaapi.get_struc_first_offset(s) - - -def GetLastMember(sid): - """ - Get offset of the last member of a structure - - @param sid: structure type ID - - @return: -1 if bad structure type ID is passed - or structure has no members - otherwise returns offset of the last member. - - @note: IDA allows 'holes' between members of a - structure. It treats these 'holes' - as unnamed arrays of bytes. - """ - s = idaapi.get_struc(sid) - if not s: - return -1 - - return idaapi.get_struc_last_offset(s) - - -def GetMemberOffset(sid, member_name): - """ - Get offset of a member of a structure by the member name - - @param sid: structure type ID - @param member_name: name of structure member - - @return: -1 if bad structure type ID is passed - or no such member in the structure - otherwise returns offset of the specified member. - """ - s = idaapi.get_struc(sid) - if not s: - return -1 - - m = idaapi.get_member_by_name(s, member_name) - if not m: - return -1 - - return m.get_soff() - - -def GetMemberName(sid, member_offset): - """ - Get name of a member of a structure - - @param sid: structure type ID - @param member_offset: member offset. The offset can be - any offset in the member. For example, - is a member is 4 bytes long and starts - at offset 2, then 2,3,4,5 denote - the same structure member. - - @return: None if bad structure type ID is passed - or no such member in the structure - otherwise returns name of the specified member. - """ - s = idaapi.get_struc(sid) - if not s: - return None - - m = idaapi.get_member(s, member_offset) - if not m: - return None - - return idaapi.get_member_name(m.id) - - -def GetMemberComment(sid, member_offset, repeatable): - """ - Get comment of a member - - @param sid: structure type ID - @param member_offset: member offset. The offset can be - any offset in the member. For example, - is a member is 4 bytes long and starts - at offset 2, then 2,3,4,5 denote - the same structure member. - @param repeatable: 1: get repeatable comment - 0: get regular comment - - @return: None if bad structure type ID is passed - or no such member in the structure - otherwise returns comment of the specified member. - """ - s = idaapi.get_struc(sid) - if not s: - return None - - m = idaapi.get_member(s, member_offset) - if not m: - return None - - return idaapi.get_member_cmt(m.id, repeatable) - - -def GetMemberSize(sid, member_offset): - """ - Get size of a member - - @param sid: structure type ID - @param member_offset: member offset. The offset can be - any offset in the member. For example, - is a member is 4 bytes long and starts - at offset 2, then 2,3,4,5 denote - the same structure member. - - @return: -1 if bad structure type ID is passed - or no such member in the structure - otherwise returns size of the specified - member in bytes. - """ - s = idaapi.get_struc(sid) - if not s: - return None - - m = idaapi.get_member(s, member_offset) - if not m: - return None - - return idaapi.get_member_size(m) - - -def GetMemberFlag(sid, member_offset): - """ - Get type of a member - - @param sid: structure type ID - @param member_offset: member offset. The offset can be - any offset in the member. For example, - is a member is 4 bytes long and starts - at offset 2, then 2,3,4,5 denote - the same structure member. - - @return: -1 if bad structure type ID is passed - or no such member in the structure - otherwise returns type of the member, see bit - definitions above. If the member type is a structure - then function GetMemberStrid() should be used to - get the structure type id. - """ - s = idaapi.get_struc(sid) - if not s: - return -1 - - m = idaapi.get_member(s, member_offset) - if not m: - return -1 - - return m.flag - - -def GetMemberStrId(sid, member_offset): - """ - Get structure id of a member - - @param sid: structure type ID - @param member_offset: member offset. The offset can be - any offset in the member. For example, - is a member is 4 bytes long and starts - at offset 2, then 2,3,4,5 denote - the same structure member. - @return: -1 if bad structure type ID is passed - or no such member in the structure - otherwise returns structure id of the member. - If the current member is not a structure, returns -1. - """ - s = idaapi.get_struc(sid) - if not s: - return -1 - - m = idaapi.get_member(s, member_offset) - if not m: - return -1 - - cs = idaapi.get_sptr(m) - if cs: - return cs.id - else: - return -1 - - -def IsUnion(sid): - """ - Is a structure a union? - - @param sid: structure type ID - - @return: 1: yes, this is a union id - 0: no - - @note: Unions are a special kind of structures - """ - s = idaapi.get_struc(sid) - if not s: - return 0 - - return s.is_union() - - -def AddStrucEx(index, name, is_union): - """ - Define a new structure type - - @param index: index of new structure type - If another structure has the specified index, - then index of that structure and all other - structures will be incremented, freeing the specifed - index. If index is == -1, then the biggest index - number will be used. - See GetFirstStrucIdx() for the explanation of - structure indices and IDs. - @param name: name of the new structure type. - @param is_union: 0: structure - 1: union - - @return: -1 if can't define structure type because of - bad structure name: the name is ill-formed or is - already used in the program. - otherwise returns ID of the new structure type - """ - if index == -1: - index = BADADDR - - return idaapi.add_struc(index, name, is_union) - - -def DelStruc(sid): - """ - Delete a structure type - - @param sid: structure type ID - - @return: 0 if bad structure type ID is passed - 1 otherwise the structure type is deleted. All data - and other structure types referencing to the - deleted structure type will be displayed as array - of bytes. - """ - s = idaapi.get_struc(sid) - if not s: - return 0 - - return idaapi.del_struc(s) - - -def SetStrucIdx(sid, index): - """ - Change structure index - - @param sid: structure type ID - @param index: new index of the structure - - @return: != 0 - ok - - @note: See GetFirstStrucIdx() for the explanation of - structure indices and IDs. - """ - s = idaapi.get_struc(sid) - if not s: - return 0 - - return idaapi.set_struc_idx(s, index) - - -def SetStrucName(sid, name): - """ - Change structure name - - @param sid: structure type ID - @param name: new name of the structure - - @return: != 0 - ok - """ - return idaapi.set_struc_name(sid, name) - - -def SetStrucComment(sid, comment, repeatable): - """ - Change structure comment - - @param sid: structure type ID - @param comment: new comment of the structure - @param repeatable: 1: change repeatable comment - 0: change regular comment - @return: != 0 - ok - """ - return idaapi.set_struc_cmt(sid, comment, repeatable) - - -def AddStrucMember(sid, name, offset, flag, typeid, nbytes, target=-1, tdelta=0, reftype=REF_OFF32): - """ - Add structure member - - @param sid: structure type ID - @param name: name of the new member - @param offset: offset of the new member - -1 means to add at the end of the structure - @param flag: type of the new member. Should be one of - FF_BYTE..FF_PACKREAL (see above) combined with FF_DATA - @param typeid: if isStruc(flag) then typeid specifies - the structure id for the member - if isOff0(flag) then typeid specifies the offset base. - if isASCII(flag) then typeid specifies the string type (ASCSTR_...). - if isStroff(flag) then typeid specifies the structure id - Otherwise should be -1. - @param nbytes: number of bytes in the new member - - @param target: target address of the offset expr. You may specify it as - -1, ida will calculate it itself - @param tdelta: offset target delta. usually 0 - @param reftype: see REF_... definitions - - @note: The remaining arguments are allowed only if isOff0(flag) and you want - to specify a complex offset expression - - @return: 0 - ok, otherwise error code (one of STRUC_ERROR_*) - - """ - if isOff0(flag): - return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d, %d, %d, %d);' % (sid, name, offset, flag, typeid, nbytes, - target, tdelta, reftype)) - else: - return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d);' % (sid, name, offset, flag, typeid, nbytes)) - - -STRUC_ERROR_MEMBER_NAME = -1 # already has member with this name (bad name) -STRUC_ERROR_MEMBER_OFFSET = -2 # already has member at this offset -STRUC_ERROR_MEMBER_SIZE = -3 # bad number of bytes or bad sizeof(type) -STRUC_ERROR_MEMBER_TINFO = -4 # bad typeid parameter -STRUC_ERROR_MEMBER_STRUCT = -5 # bad struct id (the 1st argument) -STRUC_ERROR_MEMBER_UNIVAR = -6 # unions can't have variable sized members -STRUC_ERROR_MEMBER_VARLAST = -7 # variable sized member should be the last member in the structure - - -def DelStrucMember(sid, member_offset): - """ - Delete structure member - - @param sid: structure type ID - @param member_offset: offset of the member - - @return: != 0 - ok. - - @note: IDA allows 'holes' between members of a - structure. It treats these 'holes' - as unnamed arrays of bytes. - """ - s = idaapi.get_struc(sid) - if not s: - return 0 - - return idaapi.del_struc_member(s, member_offset) - - -def SetMemberName(sid, member_offset, name): - """ - Change structure member name - - @param sid: structure type ID - @param member_offset: offset of the member - @param name: new name of the member - - @return: != 0 - ok. - """ - s = idaapi.get_struc(sid) - if not s: - return 0 - - return idaapi.set_member_name(s, member_offset, name) - - -def SetMemberType(sid, member_offset, flag, typeid, nitems, target=-1, tdelta=0, reftype=REF_OFF32): - """ - Change structure member type - - @param sid: structure type ID - @param member_offset: offset of the member - @param flag: new type of the member. Should be one of - FF_BYTE..FF_PACKREAL (see above) combined with FF_DATA - @param typeid: structure id if 'flag' == FF_STRU - Denotes type of the member is the member - itself is a structure. Otherwise should be -1. - if isOff0(flag) then typeid specifies the offset base. - if isASCII(flag) then typeid specifies the string type - (ASCSTR_...). - @param nitems: number of items in the member - - @param target: target address of the offset expr. You may specify it as - -1, ida will calculate it itself - @param tdelta: offset target delta. usually 0 - @param reftype: see REF_... definitions - - @note: The remaining arguments are allowed only if isOff0(flag) and you want - to specify a complex offset expression - - @return: !=0 - ok. - """ - if isOff0(flag): - return Eval('SetMemberType(%d, %d, %d, %d, %d, %d, %d, %d);' % (sid, member_offset, flag, typeid, nitems, - target, tdelta, reftype)) - else: - return Eval('SetMemberType(%d, %d, %d, %d, %d);' % (sid, member_offset, flag, typeid, nitems)) - - -def SetMemberComment(sid, member_offset, comment, repeatable): - """ - Change structure member comment - - @param sid: structure type ID - @param member_offset: offset of the member - @param comment: new comment of the structure member - @param repeatable: 1: change repeatable comment - 0: change regular comment - - @return: != 0 - ok - """ - s = idaapi.get_struc(sid) - if not s: - return 0 - - m = idaapi.get_member(s, member_offset) - if not m: - return 0 - - return idaapi.set_member_cmt(m, comment, repeatable) - - -def GetFchunkAttr(ea, attr): - """ - Get a function chunk attribute - - @param ea: any address in the chunk - @param attr: one of: FUNCATTR_START, FUNCATTR_END, FUNCATTR_OWNER, FUNCATTR_REFQTY - - @return: desired attribute or -1 - """ - return Eval("GetFchunkAttr(0x%x, %d);" % (ea, attr)) - - -def SetFchunkAttr(ea, attr, value): - """ - Set a function chunk attribute - - @param ea: any address in the chunk - @param attr: only FUNCATTR_START, FUNCATTR_END, FUNCATTR_OWNER - @param value: desired value - - @return: 0 if failed, 1 if success - """ - if attr in [ FUNCATTR_START, FUNCATTR_END, FUNCATTR_OWNER ]: - chunk = idaapi.get_fchunk(ea) - if chunk: - _IDC_SetAttr(chunk, _FUNCATTRMAP, attr, value) - return idaapi.update_func(chunk) - return 0 - - -def GetFchunkReferer(ea, idx): - """ - Get a function chunk referer - - @param ea: any address in the chunk - @param idx: referer index (0..GetFchunkAttr(FUNCATTR_REFQTY)) - - @return: referer address or BADADDR - """ - return idaapi.get_fchunk_referer(ea, idx) - - -def NextFchunk(ea): - """ - Get next function chunk - - @param ea: any address - - @return: the starting address of the next function chunk or BADADDR - - @note: This function enumerates all chunks of all functions in the database - """ - func = idaapi.get_next_fchunk(ea) - - if func: - return func.startEA - else: - return BADADDR - - -def PrevFchunk(ea): - """ - Get previous function chunk - - @param ea: any address - - @return: the starting address of the function chunk or BADADDR - - @note: This function enumerates all chunks of all functions in the database - """ - func = idaapi.get_prev_fchunk(ea) - - if func: - return func.startEA - else: - return BADADDR - - -def AppendFchunk(funcea, ea1, ea2): - """ - Append a function chunk to the function - - @param funcea: any address in the function - @param ea1: start of function tail - @param ea2: end of function tail - @return: 0 if failed, 1 if success - - @note: If a chunk exists at the specified addresses, it must have exactly - the specified boundaries - """ - func = idaapi.get_func(funcea) - - if not func: - return 0 - else: - return idaapi.append_func_tail(func, ea1, ea2) - - -def RemoveFchunk(funcea, tailea): - """ - Remove a function chunk from the function - - @param funcea: any address in the function - @param tailea: any address in the function chunk to remove - - @return: 0 if failed, 1 if success - """ - func = idaapi.get_func(funcea) - - if not func: - return 0 - else: - return idaapi.remove_func_tail(func, tailea) - - -def SetFchunkOwner(tailea, funcea): - """ - Change the function chunk owner - - @param tailea: any address in the function chunk - @param funcea: the starting address of the new owner - - @return: 0 if failed, 1 if success - - @note: The new owner must already have the chunk appended before the call - """ - tail = idaapi.get_func(tailea) - - if not tail: - return 0 - else: - return idaapi.set_tail_owner(tail, funcea) - - -def FirstFuncFchunk(funcea): - """ - Get the first function chunk of the specified function - - @param funcea: any address in the function - - @return: the function entry point or BADADDR - - @note: This function returns the first (main) chunk of the specified function - """ - func = idaapi.get_func(funcea) - fci = idaapi.func_tail_iterator_t(func, funcea) - if fci.main(): - return fci.chunk().startEA - else: - return BADADDR - - -def NextFuncFchunk(funcea, tailea): - """ - Get the next function chunk of the specified function - - @param funcea: any address in the function - @param tailea: any address in the current chunk - - @return: the starting address of the next function chunk or BADADDR - - @note: This function returns the next chunk of the specified function - """ - func = idaapi.get_func(funcea) - fci = idaapi.func_tail_iterator_t(func, funcea) - if not fci.main(): - return BADADDR - - # Iterate and try to find the current chunk - found = False - while True: - if fci.chunk().startEA <= tailea and \ - fci.chunk().endEA > tailea: - found = True - break - if not fci.next(): - break - - # Return the next chunk, if there is one - if found and fci.next(): - return fci.chunk().startEA - else: - return BADADDR - - -# ---------------------------------------------------------------------------- -# E N U M S -# ---------------------------------------------------------------------------- -def GetEnumQty(): - """ - Get number of enum types - - @return: number of enumerations - """ - return idaapi.get_enum_qty() - - -def GetnEnum(idx): - """ - Get ID of the specified enum by its serial number - - @param idx: number of enum (0..GetEnumQty()-1) - - @return: ID of enum or -1 if error - """ - return idaapi.getn_enum(idx) - - -def GetEnumIdx(enum_id): - """ - Get serial number of enum by its ID - - @param enum_id: ID of enum - - @return: (0..GetEnumQty()-1) or -1 if error - """ - return idaapi.get_enum_idx(enum_id) - - -def GetEnum(name): - """ - Get enum ID by the name of enum - - Arguments: - name - name of enum - - returns: ID of enum or -1 if no such enum exists - """ - return idaapi.get_enum(name) - - -def GetEnumName(enum_id): - """ - Get name of enum - - @param enum_id: ID of enum - - @return: name of enum or empty string - """ - return idaapi.get_enum_name(enum_id) - - -def GetEnumCmt(enum_id, repeatable): - """ - Get comment of enum - - @param enum_id: ID of enum - @param repeatable: 0:get regular comment - 1:get repeatable comment - - @return: comment of enum - """ - return idaapi.get_enum_cmt(enum_id, repeatable) - - -def GetEnumSize(enum_id): - """ - Get size of enum - - @param enum_id: ID of enum - - @return: number of constants in the enum - Returns 0 if enum_id is bad. - """ - return idaapi.get_enum_size(enum_id) - - -def GetEnumWidth(enum_id): - """ - Get width of enum elements - - @param enum_id: ID of enum - - @return: log2(size of enum elements in bytes)+1 - possible returned values are 1..7 - 1-1byte,2-2bytes,3-4bytes,4-8bytes,etc - Returns 0 if enum_id is bad or the width is unknown. - """ - return idaapi.get_enum_width(enum_id) - - -def GetEnumFlag(enum_id): - """ - Get flag of enum - - @param enum_id: ID of enum - - @return: flags of enum. These flags determine representation - of numeric constants (binary,octal,decimal,hex) - in the enum definition. See start of this file for - more information about flags. - Returns 0 if enum_id is bad. - """ - return idaapi.get_enum_flag(enum_id) - - -def GetConstByName(name): - """ - Get member of enum - a symbolic constant ID - - @param name: name of symbolic constant - - @return: ID of constant or -1 - """ - return idaapi.get_const_by_name(name) - - -def GetConstValue(const_id): - """ - Get value of symbolic constant - - @param const_id: id of symbolic constant - - @return: value of constant or 0 - """ - return idaapi.get_const_value(const_id) - - -def GetConstBmask(const_id): - """ - Get bit mask of symbolic constant - - @param const_id: id of symbolic constant - - @return: bitmask of constant or 0 - ordinary enums have bitmask = -1 - """ - return idaapi.get_const_bmask(const_id) - - -def GetConstEnum(const_id): - """ - Get id of enum by id of constant - - @param const_id: id of symbolic constant - - @return: id of enum the constant belongs to. - -1 if const_id is bad. - """ - return idaapi.get_const_enum(const_id) - - -def GetConstEx(enum_id, value, serial, bmask): - """ - Get id of constant - - @param enum_id: id of enum - @param value: value of constant - @param serial: serial number of the constant in the - enumeration. See OpEnumEx() for details. - @param bmask: bitmask of the constant - ordinary enums accept only -1 as a bitmask - - @return: id of constant or -1 if error - """ - return idaapi.get_const(enum_id, value, serial, bmask) - - -def GetFirstBmask(enum_id): - """ - Get first bitmask in the enum (bitfield) - - @param enum_id: id of enum (bitfield) - - @return: the smallest bitmask of constant or -1 - no bitmasks are defined yet - All bitmasks are sorted by their values - as unsigned longs. - """ - return idaapi.get_first_bmask(enum_id) - - -def GetLastBmask(enum_id): - """ - Get last bitmask in the enum (bitfield) - - @param enum_id: id of enum - - @return: the biggest bitmask or -1 no bitmasks are defined yet - All bitmasks are sorted by their values as unsigned longs. - """ - return idaapi.get_last_bmask(enum_id) - - -def GetNextBmask(enum_id, value): - """ - Get next bitmask in the enum (bitfield) - - @param enum_id: id of enum - @param value: value of the current bitmask - - @return: value of a bitmask with value higher than the specified - value. -1 if no such bitmasks exist. - All bitmasks are sorted by their values - as unsigned longs. - """ - return idaapi.get_next_bmask(enum_id, value) - - -def GetPrevBmask(enum_id, value): - """ - Get prev bitmask in the enum (bitfield) - - @param enum_id: id of enum - @param value: value of the current bitmask - - @return: value of a bitmask with value lower than the specified - value. -1 no such bitmasks exist. - All bitmasks are sorted by their values as unsigned longs. - """ - return idaapi.get_prev_bmask(enum_id, value) - - -def GetBmaskName(enum_id, bmask): - """ - Get bitmask name (only for bitfields) - - @param enum_id: id of enum - @param bmask: bitmask of the constant - - @return: name of bitmask or None - """ - return idaapi.get_bmask_name(enum_id, bmask) - - -def GetBmaskCmt(enum_id, bmask, repeatable): - """ - Get bitmask comment (only for bitfields) - - @param enum_id: id of enum - @param bmask: bitmask of the constant - @param repeatable: type of comment, 0-regular, 1-repeatable - - @return: comment attached to bitmask or None - """ - return idaapi.get_bmask_cmt(enum_id, bmask, repeatable) - - -def SetBmaskName(enum_id, bmask, name): - """ - Set bitmask name (only for bitfields) - - @param enum_id: id of enum - @param bmask: bitmask of the constant - @param name: name of bitmask - - @return: 1-ok, 0-failed - """ - return idaapi.set_bmask_name(enum_id, bmask, name) - - -def SetBmaskCmt(enum_id, bmask, cmt, repeatable): - """ - Set bitmask comment (only for bitfields) - - @param enum_id: id of enum - @param bmask: bitmask of the constant - @param cmt: comment - repeatable - type of comment, 0-regular, 1-repeatable - - @return: 1-ok, 0-failed - """ - return idaapi.set_bmask_cmt(enum_id, bmask, cmt, repeatable) - - -def GetFirstConst(enum_id, bmask): - """ - Get first constant in the enum - - @param enum_id: id of enum - @param bmask: bitmask of the constant (ordinary enums accept only -1 as a bitmask) - - @return: value of constant or -1 no constants are defined - All constants are sorted by their values as unsigned longs. - """ - return idaapi.get_first_const(enum_id, bmask) - - -def GetLastConst(enum_id, bmask): - """ - Get last constant in the enum - - @param enum_id: id of enum - @param bmask: bitmask of the constant (ordinary enums accept only -1 as a bitmask) - - @return: value of constant or -1 no constants are defined - All constants are sorted by their values - as unsigned longs. - """ - return idaapi.get_last_const(enum_id, bmask) - - -def GetNextConst(enum_id, value, bmask): - """ - Get next constant in the enum - - @param enum_id: id of enum - @param bmask: bitmask of the constant ordinary enums accept only -1 as a bitmask - @param value: value of the current constant - - @return: value of a constant with value higher than the specified - value. -1 no such constants exist. - All constants are sorted by their values as unsigned longs. - """ - return idaapi.get_next_const(enum_id, value, bmask) - - -def GetPrevConst(enum_id, value, bmask): - """ - Get prev constant in the enum - - @param enum_id: id of enum - @param bmask : bitmask of the constant - ordinary enums accept only -1 as a bitmask - @param value: value of the current constant - - @return: value of a constant with value lower than the specified - value. -1 no such constants exist. - All constants are sorted by their values as unsigned longs. - """ - return idaapi.get_prev_const(enum_id, value, bmask) - - -def GetConstName(const_id): - """ - Get name of a constant - - @param const_id: id of const - - Returns: name of constant - """ - name = idaapi.get_const_name(const_id) - - if not name: - return "" - else: - return name - - -def GetConstCmt(const_id, repeatable): - """ - Get comment of a constant - - @param const_id: id of const - @param repeatable: 0:get regular comment, 1:get repeatable comment - - @return: comment string - """ - cmt = idaapi.get_const_cmt(const_id, repeatable) - - if not cmt: - return "" - else: - return cmt - - -def AddEnum(idx, name, flag): - """ - Add a new enum type - - @param idx: serial number of the new enum. - If another enum with the same serial number - exists, then all enums with serial - numbers >= the specified idx get their - serial numbers incremented (in other words, - the new enum is put in the middle of the list of enums). - - If idx >= GetEnumQty() or idx == -1 - then the new enum is created at the end of - the list of enums. - @param name: name of the enum. - @param flag: flags for representation of numeric constants - in the definition of enum. - - @return: id of new enum or -1. - """ - return idaapi.add_enum(idx, name, flag) - - -def DelEnum(enum_id): - """ - Delete enum type - - @param enum_id: id of enum - - @return: None - """ - idaapi.del_enum(enum_id) - - -def SetEnumIdx(enum_id, idx): - """ - Give another serial number to a enum - - @param enum_id: id of enum - @param idx: new serial number. - If another enum with the same serial number - exists, then all enums with serial - numbers >= the specified idx get their - serial numbers incremented (in other words, - the new enum is put in the middle of the list of enums). - - If idx >= GetEnumQty() then the enum is - moved to the end of the list of enums. - - @return: comment string - """ - return idaapi.set_enum_idx(enum_id, idx) - - -def SetEnumName(enum_id, name): - """ - Rename enum - - @param enum_id: id of enum - @param name: new name of enum - - @return: 1-ok,0-failed - """ - return idaapi.set_enum_name(enum_id, name) - - -def SetEnumCmt(enum_id, cmt, repeatable): - """ - Set comment of enum - - @param enum_id: id of enum - @param cmt: new comment for the enum - @param repeatable: is the comment repeatable? - - 0:set regular comment - - 1:set repeatable comment - - @return: 1-ok,0-failed - """ - return idaapi.set_enum_cmt(enum_id, cmt, repeatable) - - -def SetEnumFlag(enum_id, flag): - """ - Set flag of enum - - @param enum_id: id of enum - @param flag: flags for representation of numeric constants - in the definition of enum. - - @return: 1-ok,0-failed - """ - return idaapi.set_enum_flag(enum_id, flag) - - -def SetEnumBf(enum_id, flag): - """ - Set bitfield property of enum - - @param enum_id: id of enum - @param flag: flags - - 1: convert to bitfield - - 0: convert to ordinary enum - - @return: 1-ok,0-failed - """ - return idaapi.set_enum_bf(enum_id, flag) - - -def SetEnumWidth(enum_id, width): - """ - Set width of enum elements - - @param enum_id: id of enum - @param width: element width in bytes - allowed values: 0-unknown - or 1..7: (log2 of the element size)+1 - - @return: 1-ok, 0-failed - """ - return idaapi.set_enum_width(enum_id, width) - - -def IsBitfield(enum_id): - """ - Is enum a bitfield? - - @param enum_id: id of enum - - @return: 1-yes, 0-no, ordinary enum - """ - return idaapi.is_bf(enum_id) - - -def AddConstEx(enum_id, name, value, bmask): - """ - Add a member of enum - a symbolic constant - - @param enum_id: id of enum - @param name: name of symbolic constant. Must be unique in the program. - @param value: value of symbolic constant. - @param bmask: bitmask of the constant - ordinary enums accept only -1 as a bitmask - all bits set in value should be set in bmask too - - @return: 0-ok, otherwise error code (one of CONST_ERROR_*) - """ - return idaapi.add_const(enum_id, name, value, bmask) - - -CONST_ERROR_NAME = idaapi.CONST_ERROR_NAME # already have member with this name (bad name) -CONST_ERROR_VALUE = idaapi.CONST_ERROR_VALUE # already have member with this value -CONST_ERROR_ENUM = idaapi.CONST_ERROR_ENUM # bad enum id -CONST_ERROR_MASK = idaapi.CONST_ERROR_MASK # bad bmask -CONST_ERROR_ILLV = idaapi.CONST_ERROR_ILLV # bad bmask and value combination (~bmask & value != 0) - - -def DelConstEx(enum_id, value, serial, bmask): - """ - Delete a member of enum - a symbolic constant - - @param enum_id: id of enum - @param value: value of symbolic constant. - @param serial: serial number of the constant in the - enumeration. See OpEnumEx() for for details. - @param bmask: bitmask of the constant ordinary enums accept - only -1 as a bitmask - - @return: 1-ok, 0-failed - """ - return idaapi.del_const(enum_id, value, serial, bmask) - - -def SetConstName(const_id, name): - """ - Rename a member of enum - a symbolic constant - - @param const_id: id of const - @param name: new name of constant - - @return: 1-ok, 0-failed - """ - return idaapi.set_const_name(const_id, name) - - -def SetConstCmt(const_id, cmt, repeatable): - """ - Set a comment of a symbolic constant - - @param const_id: id of const - @param cmt: new comment for the constant - @param repeatable: is the comment repeatable? - 0: set regular comment - 1: set repeatable comment - - @return: 1-ok, 0-failed - """ - return idaapi.set_const_cmt(const_id, cmt, repeatable) - -#---------------------------------------------------------------------------- -# A R R A Y S I N I D C -#---------------------------------------------------------------------------- - -def CreateArray(name): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetArrayId(name): - raise DeprecatedIDCError, "Use python pickles instead." - -def RenameArray(hashid, newname): - raise DeprecatedIDCError, "Use python pickles instead." - -def DeleteArray(hashid): - raise DeprecatedIDCError, "Use python pickles instead." - -def SetArrayLong(hashid, idx, value): - raise DeprecatedIDCError, "Use python pickles instead." - -def SetArrayString(hashid, idx, s): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetArrayElement(tag, hashid, idx): - raise DeprecatedIDCError, "Use python pickles instead." - -def DelArrayElement(tag, hashid, idx): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetFirstIndex(tag, hashid): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetLastIndex(tag, hashid): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetNextIndex(tag, hashid, idx): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetPrevIndex(tag, hashid, idx): - raise DeprecatedIDCError, "Use python pickles instead." - -def SetHashLong(hashid, idx, value): - raise DeprecatedIDCError, "Use python pickles instead." - -def SetHashString(hashid, idx, value): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetHashLong(hashid, idx): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetHashString(hashid, idx): - raise DeprecatedIDCError, "Use python pickles instead." - -def DelHashElement(hashid, idx): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetFirstHashKey(hashid): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetNextHashKey(hashid, idx): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetLastHashKey(hashid): - raise DeprecatedIDCError, "Use python pickles instead." - -def GetPrevHashKey(hashid, idx): - raise DeprecatedIDCError, "Use python pickles instead." - - -#---------------------------------------------------------------------------- -# S O U R C E F I L E / L I N E N U M B E R S -#---------------------------------------------------------------------------- -def AddSourceFile(ea1, ea2, filename): - """ - Mark a range of address as belonging to a source file - An address range may belong only to one source file. - A source file may be represented by several address ranges. - - @param ea1: linear address of start of the address range - @param ea2: linear address of end of the address range - @param filename: name of source file. - - @return: 1-ok, 0-failed. - - @note: IDA can keep information about source files used to create the program. - Each source file is represented by a range of addresses. - A source file may contains several address ranges. - """ - return idaapi.add_sourcefile(ea1, ea2, filename) - - -def GetSourceFile(ea): - """ - Get name of source file occupying the given address - - @param ea: linear address - - @return: NULL - source file information is not found - otherwise returns pointer to file name - """ - return idaapi.get_sourcefile(ea) - - -def DelSourceFile(ea): - """ - Delete information about the source file - - @param ea: linear address belonging to the source file - - @return: NULL - source file information is not found - otherwise returns pointer to file name - """ - return idaapi.del_sourcefile(ea) - - -def SetLineNumber(ea, lnnum): - """ - Set source line number - - @param ea: linear address - @param lnnum: number of line in the source file - - @return: None - """ - idaapi.set_source_linnum(ea, lnnum) - - -def GetLineNumber(ea): - """ - Get source line number - - @param ea: linear address - - @return: number of line in the source file or -1 - """ - return idaapi.get_source_linnum(ea) - - -def DelLineNumber(ea): - """ - Delete information about source line number - - @param ea: linear address - - @return: None - """ - idaapi.del_source_linnum(ea) - - -#---------------------------------------------------------------------------- -# T Y P E L I B R A R I E S -#---------------------------------------------------------------------------- - -def LoadTil(name, tildir=None): - """ - Load a type library - - @param name: name of type library. - @param tildir: drectory to load the TIL from (defaults to "til/pc") - - @return: 1-ok, 0-failed. - """ - if not tildir: - tildir = "til" + os.sep + "pc" - - til = idaapi.load_til(tildir, name) - - if til: - return 1 - else: - return 0 - - -def Til2Idb(idx, type_name): - """ - Copy information from type library to database - Copy structure, union, or enum definition from the type library - to the IDA database. - - @param idx: the position of the new type in the list of - types (structures or enums) -1 means at the end of the list - @param type_name: name of type to copy - - @return: BADNODE-failed, otherwise the type id (structure id or enum id) - """ - return idaapi.til2idb(idx, type_name) - - -def GetType(ea): - """ - Get type of function/variable - - @param ea: the address of the object - - @return: type string or None if failed - """ - return idaapi.idc_get_type(ea) - -def SizeOf(typestr): - """ - Returns the size of the type. It is equivalent to IDC's sizeof(). - Use name, tp, fld = idc.ParseType() ; Sizeof(fld) to retrieve the size - @return: -1 if typestring is not valid otherwise the size of the type - """ - return idaapi.get_type_size0(idaapi.cvar.idati, typestr) - -def GuessType(ea): - """ - Guess type of function/variable - - @param ea: the address of the object, can be the structure member id too - - @return: type string or None if failed - """ - return idaapi.idc_guess_type(ea) - - -def SetType(ea, newtype): - """ - Set type of function/variable - - @param ea: the address of the object - @param newtype: the type string in C declaration form. - Must contain the closing ';' - if specified as an empty string, then the - assciated with 'ea' will be deleted - - @return: 1-ok, 0-failed. - """ - return idaapi.apply_cdecl(ea, newtype) - -def ParseType(inputtype, flags): - """ - Parse type declaration - - @param inputtype: file name or C declarations (depending on the flags) - @param flags: combination of PT_... constants or 0 - - @return: None on failure or (name, type, fields) tuple - """ - return idaapi.idc_parse_decl(idaapi.cvar.idati, inputtype, flags) - -def ParseTypes(inputtype, flags): - """ - 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 idaapi.idc_parse_types(inputtype, flags) - - -PT_FILE = 0x0001 # input if a file name (otherwise contains type declarations) -PT_SILENT = 0x0002 # silent mode -PT_PAKDEF = 0x0000 # default pack value -PT_PAK1 = 0x0010 # #pragma pack(1) -PT_PAK2 = 0x0020 # #pragma pack(2) -PT_PAK4 = 0x0030 # #pragma pack(4) -PT_PAK8 = 0x0040 # #pragma pack(8) -PT_PAK16 = 0x0050 # #pragma pack(16) - - -def GetMaxLocalType(): - """ - Get number of local types + 1 - - @return: value >= 1. 1 means that there are no local types. - """ - return idaapi.get_ordinal_qty(idaapi.cvar.idati) - - -def SetLocalType(ordinal, input, flags): - """ - Parse one type declaration and store it in the specified slot - - @param ordinal: slot number (1...NumberOfLocalTypes) - -1 means allocate new slot or reuse the slot - of the existing named type - @param input: C declaration. Empty input empties the slot - @param flags: combination of PT_... constants or 0 - - @return: slot number or 0 if error - """ - return idaapi.idc_set_local_type(ordinal, input, flags) - - -def GetLocalType(ordinal, flags): - """ - Retrieve a local type declaration - - @param ordinal: slot number (1...NumberOfLocalTypes) - @param flags: any of PRTYPE_* constants - - @return: local type as a C declaration or "" - - @note: This function can return types strings up to 64KiB. Use idaapi.idc_get_local_type() - for larger types. - """ - res,str = idaapi.idc_get_local_type(ordinal, flags, 2**16) - return str - -PRTYPE_1LINE = 0x0000 # print to one line -PRTYPE_MULTI = 0x0001 # print to many lines -PRTYPE_TYPE = 0x0002 # print type declaration (not variable declaration) -PRTYPE_PRAGMA = 0x0004 # print pragmas for alignment - - -def GetLocalTypeName(ordinal): - """ - Retrieve a local type name - - @param ordinal: slot number (1...NumberOfLocalTypes) - - returns: local type name or None - """ - return idaapi.idc_get_local_type_name(ordinal) - - -# ---------------------------------------------------------------------------- -# H I D D E N A R E A S -# ---------------------------------------------------------------------------- -def HideArea(start, end, description, header, footer, color): - """ - Hide an area - - Hidden areas - address ranges which can be replaced by their descriptions - - @param start: area start - @param end: area end - @param description: description to display if the area is collapsed - @param header: header lines to display if the area is expanded - @param footer: footer lines to display if the area is expanded - @param color: RGB color code (-1 means default color) - - @returns: !=0 - ok - """ - return idaapi.add_hidden_area(start, end, description, header, footer, color) - - -def SetHiddenArea(ea, visible): - """ - Set hidden area state - - @param ea: any address belonging to the hidden area - @param visible: new state of the area - - @return: != 0 - ok - """ - ha = idaapi.get_hidden_area(ea) - - if not ha: - return 0 - else: - ha.visible = visible - return idaapi.update_hidden_area(ha) - - -def DelHiddenArea(ea): - """ - Delete a hidden area - - @param ea: any address belonging to the hidden area - @returns: != 0 - ok - """ - return idaapi.del_hidden_area(ea) - - -#-------------------------------------------------------------------------- -# D E B U G G E R I N T E R F A C E -#-------------------------------------------------------------------------- -def LoadDebugger(dbgname, use_remote): - """ - Load the debugger - - @param dbgname: debugger module name Examples: win32, linux, mac. - @param use_remote: 0/1: use remote debugger or not - - @note: This function is needed only when running idc scripts from the command line. - In other cases IDA loads the debugger module automatically. - """ - return idaapi.load_debugger(dbgname, use_remote) - - -def StartDebugger(path, args, sdir): - """ - Launch the debugger - - @param path: path to the executable file. - @param args: command line arguments - @param sdir: initial directory for the process - - @return: -1-failed, 0-cancelled by the user, 1-ok - - @note: For all args: if empty, the default value from the database will be used - See the important note to the StepInto() function - """ - return idaapi.start_process(path, args, sdir) - - -def StopDebugger(): - """ - Stop the debugger - Kills the currently debugger process and returns to the disassembly mode - - @return: success - """ - return idaapi.exit_process() - - -def PauseProcess(): - """ - Suspend the running process - Tries to suspend the process. If successful, the PROCESS_SUSPEND - debug event will arrive (see GetDebuggerEvent) - - @return: success - - @note: To resume a suspended process use the GetDebuggerEvent function. - See the important note to the StepInto() function - """ - return idaapi.suspend_process() - - -def GetProcessQty(): - """ - Take a snapshot of running processes and return their number. - """ - return idaapi.get_process_qty() - - -def GetProcessPid(idx): - """ - Get the process ID of a running process - - @param idx: number of process, is in range 0..GetProcessQty()-1 - - @return: 0 if failure - """ - pinfo = idaapi.process_info_t() - pid = idaapi.get_process_info(idx, pinfo) - if pid != idaapi.NO_PROCESS: - return pinfo.pid - else: - return 0 - - -def GetProccessName(idx): - """ - Get the name of a running process - - @param idx: number of process, is in range 0..GetProcessQty()-1 - - @return: None if failure - """ - pinfo = idaapi.process_info_t() - pid = idaapi.get_process_info(idx, pinfo) - if pid != idaapi.NO_PROCESS: - return pinfo.name - else: - return "" - - -def AttachProcess(pid, event_id): - """ - Attach the debugger to a running process - - @param pid: PID of the process to attach to. If NO_PROCESS, a dialog box - will interactively ask the user for the process to attach to. - @param event_id: reserved, must be -1 - - @return: - - -2: impossible to find a compatible process - - -1: impossible to attach to the given process (process died, privilege - needed, not supported by the debugger plugin, ...) - - 0: the user cancelled the attaching to the process - - 1: the debugger properly attached to the process - @note: See the important note to the StepInto() function - """ - return idaapi.attach_process(pid, event_id) - - -def DetachProcess(): - """ - Detach the debugger from the debugged process. - - @return: success - """ - return idaapi.detach_process() - - -def GetThreadQty(): - """ - Get number of threads. - - @return: number of threads - """ - return idaapi.get_thread_qty() - - -def GetThreadId(idx): - """ - Get the ID of a thread - - @param idx: number of thread, is in range 0..GetThreadQty()-1 - - @return: -1 if failure - """ - return idaapi.getn_thread(idx) - - -def GetCurrentThreadId(): - """ - Get current thread ID - - @return: -1 if failure - """ - return idaapi.get_current_thread() - - -def SelectThread(tid): - """ - Select the given thread as the current debugged thread. - - @param tid: ID of the thread to select - - @return: success - - @note: The process must be suspended to select a new thread. - """ - return idaapi.select_thread(tid) - - -def SuspendThread(tid): - """ - Suspend thread - - @param tid: thread id - - @return: -1:network error, 0-failed, 1-ok - - @note: Suspending a thread may deadlock the whole application if the suspended - was owning some synchronization objects. - """ - return idaapi.suspend_thread(tid) - - -def ResumeThread(tid): - """ - Resume thread - - @param tid: thread id - - @return: -1:network error, 0-failed, 1-ok - """ - return idaapi.resume_thread(tid) - - -def _get_modules(): - """ - INTERNAL: Enumerate process modules - """ - module = idaapi.module_info_t() - result = idaapi.get_first_module(module) - while result: - yield module - result = idaapi.get_next_module(module) - - -def GetFirstModule(): - """ - Enumerate process modules - - @return: first module's base address or None on failure - """ - for module in _get_modules(): - return module.base - else: - return None - - -def GetNextModule(base): - """ - Enumerate process modules - - @param base: previous module's base address - - @return: next module's base address or None on failure - """ - foundit = False - for module in _get_modules(): - if foundit: - return module.base - if module.base == base: - foundit = True - else: - return None - - -def GetModuleName(base): - """ - Get process module name - - @param base: the base address of the module - - @return: required info or 0 - """ - for module in _get_modules(): - if module.base == base: - return module.name - else: - return 0 - - -def GetModuleSize(base): - """ - Get process module size - - @param base: the base address of the module - - @return: required info or -1 - """ - for module in _get_modules(): - if module.base == base: - return module.size - else: - return -1 - - -def StepInto(): - """ - Execute one instruction in the current thread. - Other threads are kept suspended. - - @return: success - - @note: You must call GetDebuggerEvent() after this call - in order to find out what happened. Normally you will - get the STEP event but other events are possible (for example, - an exception might occur or the process might exit). - This remark applies to all execution control functions. - The event codes depend on the issued command. - """ - return idaapi.step_into() - - -def StepOver(): - """ - Execute one instruction in the current thread, - but without entering into functions - Others threads keep suspended. - See the important note to the StepInto() function - - @return: success - """ - return idaapi.step_over() - - -def RunTo(ea): - """ - Execute the process until the given address is reached. - If no process is active, a new process is started. - See the important note to the StepInto() function - - @return: success - """ - return idaapi.run_to(ea) - - -def StepUntilRet(): - """ - Execute instructions in the current thread until - a function return instruction is reached. - Other threads are kept suspended. - See the important note to the StepInto() function - - @return: success - """ - return idaapi.step_until_ret() - - -def GetDebuggerEvent(wfne, timeout): - """ - Wait for the next event - This function (optionally) resumes the process - execution and wait for a debugger event until timeout - - @param wfne: combination of WFNE_... constants - @param timeout: number of seconds to wait, -1-infinity - - @return: debugger event codes, see below - """ - return idaapi.wait_for_next_event(wfne, timeout) - - -def ResumeProcess(): - return GetDebuggerEvent(WFNE_CONT|WFNE_NOWAIT, 0) - -def SendDbgCommand(cmd): - """Sends a command to the debugger module 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 - -# wfne flag is combination of the following: -WFNE_ANY = 0x0001 # return the first event (even if it doesn't suspend the process) - # if the process is still running, the database - # does not reflect the memory state. you might want - # to call RefreshDebuggerMemory() in this case -WFNE_SUSP = 0x0002 # wait until the process gets suspended -WFNE_SILENT = 0x0004 # 1: be slient, 0:display modal boxes if necessary -WFNE_CONT = 0x0008 # continue from the suspended state -WFNE_NOWAIT = 0x0010 # do not wait for any event, immediately return DEC_TIMEOUT - # (to be used with WFNE_CONT) - -# debugger event codes -NOTASK = -2 # process does not exist -DBG_ERROR = -1 # error (e.g. network problems) -DBG_TIMEOUT = 0 # timeout -PROCESS_START = 0x00000001 # New process started -PROCESS_EXIT = 0x00000002 # Process stopped -THREAD_START = 0x00000004 # New thread started -THREAD_EXIT = 0x00000008 # Thread stopped -BREAKPOINT = 0x00000010 # Breakpoint reached -STEP = 0x00000020 # One instruction executed -EXCEPTION = 0x00000040 # Exception -LIBRARY_LOAD = 0x00000080 # New library loaded -LIBRARY_UNLOAD = 0x00000100 # Library unloaded -INFORMATION = 0x00000200 # User-defined information -SYSCALL = 0x00000400 # Syscall (not used yet) -WINMESSAGE = 0x00000800 # Window message (not used yet) -PROCESS_ATTACH = 0x00001000 # Attached to running process -PROCESS_DETACH = 0x00002000 # Detached from process -PROCESS_SUSPEND = 0x00004000 # Process has been suspended - - -def RefreshDebuggerMemory(): - """ - Refresh debugger memory - Upon this call IDA will forget all cached information - about the debugged process. This includes the segmentation - information and memory contents (register cache is managed - automatically). Also, this function refreshes exported name - from loaded DLLs. - You must call this function before using the segmentation - information, memory contents, or names of a non-suspended process. - This is an expensive call. - """ - return idaapi.refresh_debugger_memory() - - -def TakeMemorySnapshot(only_loader_segs): - """ - Take memory snapshot of the debugged process - - @param only_loader_segs: 0-copy all segments to idb - 1-copy only SFL_LOADER segments - """ - return idaapi.take_memory_snapshot(only_loader_segs) - - -def GetProcessState(): - """ - Get debugged process state - - @return: one of the DBG_... constants (see below) - """ - return idaapi.get_process_state() - -DSTATE_SUSP_FOR_EVENT = -2 # process is currently suspended to react to a debug event -DSTATE_SUSP = -1 # process is suspended -DSTATE_NOTASK = 0 # no process is currently debugged -DSTATE_RUN = 1 # process is running -DSTATE_RUN_WAIT_ATTACH = 2 # process is running, waiting for process properly attached -DSTATE_RUN_WAIT_END = 3 # process is running, but the user asked to kill/detach the process - # remark: in this case, most events are ignored - -""" - Get various information about the current debug event - These functions are valid only when the current event exists - (the process is in the suspended state) -""" - -# For all events: - -def GetEventId(): - """ - Get ID of debug event - - @return: event ID - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return ev.eid - - -def GetEventPid(): - """ - Get process ID for debug event - - @return: process ID - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return ev.pid - - -def GetEventTid(): - """ - Get type ID for debug event - - @return: type ID - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return ev.tid - - -def GetEventEa(): - """ - Get ea for debug event - - @return: ea - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return ev.ea - - -def IsEventHandled(): - """ - Is the debug event handled? - - @return: boolean - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return ev.handled - - -# For PROCESS_START, PROCESS_ATTACH, LIBRARY_LOAD events: - -def GetEventModuleName(): - """ - Get module name for debug event - - @return: module name - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return idaapi.get_event_module_name(ev) - - -def GetEventModuleBase(): - """ - Get module base for debug event - - @return: module base - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return idaapi.get_event_module_base(ev) - - -def GetEventModuleSize(): - """ - Get module size for debug event - - @return: module size - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return idaapi.get_event_module_size(ev) - - -def GetEventExitCode(): - """ - Get exit code for debug event - - @return: exit code for PROCESS_EXIT, THREAD_EXIT events - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return ev.exit_code - - -def GetEventInfo(): - """ - Get debug event info - - @return: event info: for LIBRARY_UNLOAD (unloaded library name) - for INFORMATION (message to display) - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return idaapi.get_event_info(ev) - - -def GetEventBptHardwareEa(): - """ - Get hardware address for BREAKPOINT event - - @return: hardware address - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return idaapi.get_event_bpt_hea(ev) - - -def GetEventExceptionCode(): - """ - Get exception code for EXCEPTION event - - @return: exception code - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return idaapi.get_event_exc_code(ev) - - -def GetEventExceptionEa(): - """ - Get address for EXCEPTION event - - @return: adress of exception - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return idaapi.get_event_exc_ea(ev) - - -def CanExceptionContinue(): - """ - Can it continue after EXCEPTION event? - - @return: boolean - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return idaapi.can_exc_continue(ev) - - -def GetEventExceptionInfo(): - """ - Get info for EXCEPTION event - - @return: info string - """ - ev = idaapi.get_debug_event() - assert ev, "Could not retrieve debug event" - return idaapi.get_event_exc_info(ev) - - -def SetDebuggerOptions(opt): - """ - Get/set debugger options - - @param opt: combination of DOPT_... constants - - @return: old options - """ - return idaapi.set_debugger_options(opt) - - -DOPT_SEGM_MSGS = 0x00000001 # print messages on debugger segments modifications -DOPT_START_BPT = 0x00000002 # break on process start -DOPT_THREAD_MSGS = 0x00000004 # print messages on thread start/exit -DOPT_THREAD_BPT = 0x00000008 # break on thread start/exit -DOPT_BPT_MSGS = 0x00000010 # print message on breakpoint -DOPT_LIB_MSGS = 0x00000040 # print message on library load/unlad -DOPT_LIB_BPT = 0x00000080 # break on library load/unlad -DOPT_INFO_MSGS = 0x00000100 # print message on debugging information -DOPT_INFO_BPT = 0x00000200 # break on debugging information -DOPT_REAL_MEMORY = 0x00000400 # don't hide breakpoint instructions -DOPT_REDO_STACK = 0x00000800 # reconstruct the stack -DOPT_ENTRY_BPT = 0x00001000 # break on program entry point -DOPT_EXCDLG = 0x00006000 # exception dialogs: - -EXCDLG_NEVER = 0x00000000 # never display exception dialogs -EXCDLG_UNKNOWN = 0x00002000 # display for unknown exceptions -EXCDLG_ALWAYS = 0x00006000 # always display - -DOPT_LOAD_DINFO = 0x00008000 # automatically load debug files (pdb) - - -def SetRemoteDebugger(hostname, password, portnum): - """ - Set remote debugging options - - @param hostname: remote host name or address if empty, revert to local debugger - @param password: password for the debugger server - @param portnum: port number to connect (-1: don't change) - - @return: nothing - """ - return idaapi.set_remote_debugger(hostname, password, portnum) - - -def GetExceptionQty(): - """ - Get number of defined exception codes - """ - return idaapi.get_exception_qty() - - -def GetExceptionCode(idx): - """ - Get exception code - - @param idx: number of exception in the vector (0..GetExceptionQty()-1) - - @return: exception code (0 - error) - """ - return idaapi.get_exception_code(idx) - - -def GetExceptionName(code): - """ - Get exception information - - @param code: exception code - - @return: "" on error - """ - return idaapi.get_exception_name(code) - - -def GetExceptionFlags(code): - """ - Get exception information - - @param code: exception code - - @return: -1 on error - """ - return idaapi.get_exception_flags(code) - -def DefineException(code, name, desc, flags): - """ - Add exception handling information - - @param code: exception code - @param name: exception name - @param desc: exception description - @param flags: exception flags (combination of EXC_...) - - @return: failure description or "" - """ - return idaapi.define_exception(code, name, desc, flags) - -EXC_BREAK = 0x0001 # break on the exception -EXC_HANDLE = 0x0002 # should be handled by the debugger? - - -def SetExceptionFlags(code, flags): - """ - Set exception flags - - @param code: exception code - @param flags: exception flags (combination of EXC_...) - """ - return idaapi.set_exception_flags(code, flags) - - -def ForgetException(code): - """ - Delete exception handling information - - @param code: exception code - """ - return idaapi.forget_exception(code) - - -def GetRegValue(name): - """ - Get register value - - @param name: the register name - - @note: The debugger should be running. otherwise the function fails - the register name should be valid. - It is not necessary to use this function to get register values - because a register name in the script will do too. - - @return: register value (integer or floating point) - """ - rv = idaapi.regval_t() - res = idaapi.get_reg_val(name, rv) - assert res, "get_reg_val() failed, bogus name perhaps?" - return rv.ival - - -def SetRegValue(value, name): - """ - Set register value - - @param name: the register name - @param value: new register value - - @note: The debugger should be running - It is not necessary to use this function to set register values. - A register name in the left side of an assignment will do too. - """ - rv = idaapi.regval_t() - if type(value)==types.StringType: - value = int(value) - elif type(value)!=types.IntType: - print "SetRegValue: value must be integer!" - return BADADDR - - if value<0: - #ival_set cannot handle negative numbers - value &= 0xFFFFFFFF - - rv.ival = value - return idaapi.set_reg_val(name, rv) - - -def GetBptQty(): - """ - Get number of breakpoints. - - @return: number of breakpoints - """ - return idaapi.get_bpt_qty() - - -def GetBptEA(n): - """ - Get breakpoint address - - @param n: number of breakpoint, is in range 0..GetBptQty()-1 - - @return: addresss of the breakpoint or BADADDR - """ - bpt = idaapi.bpt_t() - - if idaapi.getn_bpt(n, bpt): - return bpt.ea - else: - return BADADDR - - -def GetBptAttr(ea, bptattr): - """ - Get the characteristics of a breakpoint - - @param ea: any address in the breakpoint range - @param bptattr: the desired attribute code, one of BPTATTR_... constants - - @return: the desired attribute value or -1 - """ - bpt = idaapi.bpt_t() - - if not idaapi.get_bpt(ea, bpt): - return -1 - else: - if bptattr == BPTATTR_EA: - return bpt.ea - if bptattr == BPTATTR_SIZE: - return bpt.size - if bptattr == BPTATTR_TYPE: - return bpt.type - if bptattr == BPTATTR_COUNT: - return bpt.pass_count - if bptattr == BPTATTR_FLAGS: - return bpt.flags - if bptattr == BPTATTR_COND: - return bpt.condition - return -1 - - -BPTATTR_EA = 0 # starting address of the breakpoint -BPTATTR_SIZE = 4 # size of the breakpoint (undefined for software breakpoint) -BPTATTR_TYPE = 8 # type of the breakpoint -BPTATTR_COUNT = 12 # number of times this breakpoint is hit before stopping -BPTATTR_FLAGS = 16 # Breakpoint attributes: -BPTATTR_COND = 20 # Breakpoint condition NOTE: the return value is a string in this case - -if __EA64__: - BPTATTR_SIZE = 8 - BPTATTR_TYPE = 16 - BPTATTR_COUNT = 20 - BPTATTR_FLAGS = 24 - BPTATTR_COND = 28 - -# Breakpoint types: -BPT_EXEC = 0 # Hardware: Execute instruction -BPT_WRITE = 1 # Hardware: Write access -BPT_RDWR = 3 # Hardware: Read/write access -BPT_SOFT = 4 # Software breakpoint - -BPT_BRK = 0x01 # the debugger stops on this breakpoint -BPT_TRACE = 0x02 # the debugger adds trace information when this breakpoint is reached - - -def SetBptAttr(address, bptattr, value): - """ - modifiable characteristics of a breakpoint - - @param address: any address in the breakpoint range - @param bptattr: the attribute code, one of BPTATTR_* constants - BPTATTR_CND is not allowed, see SetBptCnd() - @param value: the attibute value - - @return: success - """ - bpt = idaapi.bpt_t() - - if not idaapi.get_bpt(address, bpt): - return False - else: - if bptattr not in [ BPTATTR_SIZE, BPTATTR_TYPE, BPTATTR_FLAGS, BPTATTR_COUNT ]: - return False - if bptattr == BPTATTR_SIZE: - bpt.size = value - if bptattr == BPTATTR_TYPE: - bpt.type = value - if bptattr == BPTATTR_COUNT: - bpt.pass_count = value - if bptattr == BPTATTR_FLAGS: - bpt.flags = value - - idaapi.update_bpt(bpt) - return True - - -def SetBptCnd(ea, cnd): - """ - Set breakpoint condition - - @param ea: any address in the breakpoint range - @param cnd: breakpoint condition - - @return: success - """ - bpt = idaapi.bpt_t() - - if not idaapi.get_bpt(ea, bpt): - return False - - bpt.condition = cnd - - return idaapi.update_bpt(bpt) - - -def AddBptEx(ea, size, bpttype): - """ - Add a new breakpoint - - @param ea: any address in the process memory space: - @param size: size of the breakpoint (irrelevant for software breakpoints): - @param bpttype: type of the breakpoint (one of BPT_... constants) - - @return: success - - @note: Only one breakpoint can exist at a given address. - """ - return idaapi.add_bpt(ea, size, bpttype) - - -def AddBpt(ea): return AddBptEx(ea, 0, BPT_SOFT) - - -def DelBpt(ea): - """ - Delete breakpoint - - @param ea: any address in the process memory space: - - @return: success - """ - return idaapi.del_bpt(ea) - - -def EnableBpt(ea, enable): - """ - Enable/disable breakpoint - - @param ea: any address in the process memory space - - @return: success - - @note: Disabled breakpoints are not written to the process memory - """ - return idaapi.enable_bpt(ea, enable) - - -def CheckBpt(ea): - """ - Check a breakpoint - - @param ea: address in the process memory space - - @return: one of BPTCK_... constants - """ - return idaapi.check_bpt(ea) - -BPTCK_NONE = -1 # breakpoint does not exist -BPTCK_NO = 0 # breakpoint is disabled -BPTCK_YES = 1 # breakpoint is enabled -BPTCK_ACT = 2 # breakpoint is active (written to the process) - - -def EnableTracing(trace_level, enable): - """ - Enable step tracing - - @param trace_level: what kind of trace to modify - @param enable: 0: turn off, 1: turn on - - @return: success - """ - assert trace_level in [ TRACE_STEP, TRACE_INSN, TRACE_FUNC ], \ - "trace_level must be one of TRACE_* constants" - - if trace_level == TRACE_STEP: - return idaapi.enable_step_trace(enable) - - if trace_level == TRACE_INSN: - return idaapi.enable_insn_trace(enable) - - if trace_level == TRACE_FUNC: - return idaapi.enable_func_trace(enable) - - return False - -TRACE_STEP = 0x0 # lowest level trace. trace buffers are not maintained -TRACE_INSN = 0x1 # instruction level trace -TRACE_FUNC = 0x2 # function level trace (calls & rets) - -#-------------------------------------------------------------------------- -# C O L O R S -#-------------------------------------------------------------------------- - -def GetColor(ea, what): - """ - Get item color - - @param ea: address of the item - @param what: type of the item (one of CIC_* constants) - - @return: color code in RGB (hex 0xBBGGRR) - """ - if what not in [ CIC_ITEM, CIC_FUNC, CIC_SEGM ]: - raise ValueError, "'what' must be one of CIC_ITEM, CIC_FUNC and CIC_SEGM" - - if what == CIC_ITEM: - return idaapi.get_item_color(ea) - - if what == CIC_FUNC: - func = idaapi.get_func(ea) - if func: - return func.color - else: - return DEFCOLOR - - if what == CIC_SEGM: - seg = idaapi.getseg(ea) - if seg: - return seg.color - else: - return DEFCOLOR - -# color item codes: -CIC_ITEM = 1 # one instruction or data -CIC_FUNC = 2 # function -CIC_SEGM = 3 # segment - -DEFCOLOR = 0xFFFFFFFF # Default color - - -def SetColor(ea, what, color): - """ - Set item color - - @param ea: address of the item - @param what: type of the item (one of CIC_* constants) - @param color: new color code in RGB (hex 0xBBGGRR) - - @return: success (True or False) - """ - if what not in [ CIC_ITEM, CIC_FUNC, CIC_SEGM ]: - raise ValueError, "'what' must be one of CIC_ITEM, CIC_FUNC and CIC_SEGM" - - if what == CIC_ITEM: - return idaapi.set_item_color(ea, color) - - if what == CIC_FUNC: - func = idaapi.get_func(ea) - if func: - func.color = color - return bool(idaapi.update_func(func)) - else: - return False - - if what == CIC_SEGM: - seg = idaapi.getseg(ea) - if seg: - seg.color = color - return bool(seg.update()) - else: - return False - - -#-------------------------------------------------------------------------- -# X M L -#-------------------------------------------------------------------------- - -def SetXML(path, name, value): - """ - Set or update one or more XML values. - - @param path: XPath expression of elements where to create value(s) - @param name: name of the element/attribute - (use @XXX for an attribute) to create. - If 'name' is empty, the elements or - attributes returned by XPath are directly - updated to contain the new 'value'. - @param value: value of the element/attribute - - @return: success (True or False) - """ - return idaapi.set_xml(path, name, value) - - -def GetXML(path): - """ - Get one XML value. - - @param path: XPath expression to an element - or attribute whose value is requested - - @return: the value, None if failed - """ - v = idaapi.value_t() - if idaapi.get_xml(path): - return v.str - else: - return None - - -#---------------------------------------------------------------------------- -# A R M S P E C I F I C -#---------------------------------------------------------------------------- -def ArmForceBLJump(ea): - """ - Some ARM compilers in Thumb mode use BL (branch-and-link) - instead of B (branch) for long jumps, since BL has more range. - By default, IDA tries to determine if BL is a jump or a call. - You can override IDA's decision using commands in Edit/Other menu - (Force BL call/Force BL jump) or the following two functions. - - Force BL instruction to be a jump - - @param ea: address of the BL instruction - - @return: 1-ok, 0-failed - """ - return Eval("ArmForceBLJump(0x%x)"%ea) - - -def ArmForceBLCall(ea): - """ - Force BL instruction to be a call - - @param ea: address of the BL instruction - - @return: 1-ok, 0-failed - """ - return Eval("ArmForceBLCall(0x%x)"%ea) - - -#-------------------------------------------------------------------------- -# Compatibility macros: -def Compile(file): return CompileEx(file, 1) -def OpOffset(ea,base): return OpOff(ea,-1,base) -def OpNum(ea): return OpNumber(ea,-1) -def OpChar(ea): return OpChr(ea,-1) -def OpSegment(ea): return OpSeg(ea,-1) -def OpDec(ea): return OpDecimal(ea,-1) -def OpAlt1(ea, opstr): return OpAlt(ea, 0, opstr) -def OpAlt2(ea, opstr): return OpAlt(ea, 1, opstr) -def StringStp(x): return SetCharPrm(INF_ASCII_BREAK,x) -def LowVoids(x): return SetLongPrm(INF_LOW_OFF,x) -def HighVoids(x): return SetLongPrm(INF_HIGH_OFF,x) -def TailDepth(x): return SetLongPrm(INF_MAXREF,x) -def Analysis(x): return SetCharPrm(INF_AUTO,x) -def Tabs(x): return SetCharPrm(INF_ENTAB,x) -#def Comments(x): SetCharPrm(INF_CMTFLAG,((x) ? (SW_ALLCMT|GetCharPrm(INF_CMTFLAG)) : (~SW_ALLCMT&GetCharPrm(INF_CMTFLAG)))) -def Voids(x): return SetCharPrm(INF_VOIDS,x) -def XrefShow(x): return SetCharPrm(INF_XREFNUM,x) -def Indent(x): return SetCharPrm(INF_INDENT,x) -def CmtIndent(x): return SetCharPrm(INF_COMMENT,x) -def AutoShow(x): return SetCharPrm(INF_SHOWAUTO,x) -def MinEA(): return GetLongPrm(INF_MIN_EA) -def MaxEA(): return GetLongPrm(INF_MAX_EA) -def BeginEA(): return GetLongPrm(INF_BEGIN_EA) -def set_start_cs(x): return SetLongPrm(INF_START_CS,x) -def set_start_ip(x): return SetLongPrm(INF_START_IP,x) - -def WriteMap(filepath): - return GenerateFile(OFILE_MAP, filepath, 0, BADADDR, GENFLG_MAPSEG|GENFLG_MAPNAME) - -def WriteTxt(filepath, ea1, ea2): - return GenerateFile(OFILE_ASM, filepath, ea1, ea2, 0) - -def WriteExe(filepath): - return GenerateFile(OFILE_EXE, filepath, 0, BADADDR, 0) - -def AddConst(enum_id,name,value): return AddConstEx(enum_id,name,value,-1) -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) -#def GetFrameRegsSize(ea): return GetFunctionAttr(ea, FUNCATTR_FRREGS) -#def GetFrameArgsSize(ea): return GetFunctionAttr(ea, FUNCATTR_ARGSIZE) -#def GetFunctionFlags(ea): return GetFunctionAttr(ea, FUNCATTR_FLAGS) -#def SetFunctionFlags(ea, flags): return SetFunctionAttr(ea, FUNCATTR_FLAGS, flags) - -#def SegStart(ea): return GetSegmentAttr(ea, SEGATTR_START) -#def SegEnd(ea): return GetSegmentAttr(ea, SEGATTR_END) -#def SetSegmentType(ea, type): return SetSegmentAttr(ea, SEGATTR_TYPE, type) - -def SegCreate(a1, a2, base, use32, align, comb): return AddSeg(a1, a2, base, use32, align, comb) -def SegDelete(ea, flags): return DelSeg(ea, flags) -def SegBounds(ea, startea, endea, flags): return SetSegBounds(ea, startea, endea, flags) -def SegRename(ea, name): return RenameSeg(ea, name) -def SegClass(ea, segclass): return SetSegClass(ea, segclass) -def SegAddrng(ea, bitness): return SetSegAddressing(ea, bitness) -def SegDefReg(ea, reg, value): return SetSegDefReg(ea, reg, value) - - -def Comment(ea): return GetCommentEx(ea, 0) -def RptCmt(ea): return GetCommentEx(ea, 1) - -def SetReg(ea, reg, value): return SetRegEx(ea, reg, value, SR_user) - - -# Convenience functions: -def here(): return ScreenEA() -def isEnabled(ea): return (PrevAddr(ea+1)==ea) - -# Obsolete segdel macros: -SEGDEL_PERM = 0x0001 # permanently, i.e. disable addresses -SEGDEL_KEEP = 0x0002 # keep information (code & data, etc) -SEGDEL_SILENT = 0x0004 # be silent - -# END OF IDC COMPATIBILY CODE +#!/usr/bin/env python +#--------------------------------------------------------------------- +# IDAPython - Python plugin for Interactive Disassembler Pro +# +# Original IDC.IDC: +# Copyright (c) 1990-2010 Ilfak Guilfanov +# +# Python conversion: +# Copyright (c) 2004-2010 Gergely Erdelyi +# +# All rights reserved. +# +# For detailed copyright information see the file COPYING in +# the root of the distribution archive. +#--------------------------------------------------------------------- +# idc.py - IDC compatibility module +#--------------------------------------------------------------------- +""" +IDC compatibility module + +This file contains IDA built-in function declarations and internal bit +definitions. Each byte of the program has 32-bit flags (low 8 bits keep +the byte value). These 32 bits are used in GetFlags/SetFlags functions. +You may freely examine these bits using GetFlags() but the use of the +SetFlags() function is strongly discouraged. + +This file is subject to change without any notice. +Future versions of IDA may use other definitions. +""" +try: + import idaapi +except ImportError: + print "Could not import idaapi. Running in 'pydoc mode'." + +import os +import re +import struct +import time +import types + +__EA64__ = idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL +WORDMASK = __EA64__ and 0xFFFFFFFFFFFFFFFF or 0xFFFFFFFF + +class DeprecatedIDCError(Exception): + """ + Exception for deprecated function calls + """ + pass + + +def _IDC_GetAttr(obj, attrmap, attroffs): + """ + Internal function to generically get object attributes + Do not use unless you know what you are doing + """ + if attroffs in attrmap and hasattr(obj, attrmap[attroffs][1]): + return getattr(obj, attrmap[attroffs][1]) + else: + errormsg = "attribute with offset %d not found, check the offset and report the problem" % attroffs + raise KeyError, errormsg + + +def _IDC_SetAttr(obj, attrmap, attroffs, value): + """ + Internal function to generically set object attributes + Do not use unless you know what you are doing + """ + # check for read-only atributes + if attroffs in attrmap: + if attrmap[attroffs][0]: + raise KeyError, "attribute with offset %d is read-only" % attroffs + elif hasattr(obj, attrmap[attroffs][1]): + return setattr(obj, attrmap[attroffs][1], value) + errormsg = "attribute with offset %d not found, check the offset and report the problem" % attroffs + raise KeyError, errormsg + + +BADADDR = idaapi.BADADDR # Not allowed address value +BADSEL = idaapi.BADSEL # Not allowed selector value/number +MAXADDR = idaapi.MAXADDR & WORDMASK + +# +# Flag bit definitions (for GetFlags()) +# +MS_VAL = idaapi.MS_VAL # Mask for byte value +FF_IVL = idaapi.FF_IVL # Byte has value ? + +# Do flags contain byte value? (i.e. has the byte a value?) +# if not, the byte is uninitialized. + +def hasValue(F): return ((F & FF_IVL) != 0) # any defined value? + +# Get byte value from flags +# Get value of byte provided that the byte is initialized. +# This macro works ok only for 8-bit byte machines. + +def byteValue(F): return (F & MS_VAL) # quick replacement for Byte() + +# Is the byte initialized? + +def isLoaded(ea): return hasValue(GetFlags(ea)) # any defined value? + +MS_CLS = idaapi.MS_CLS # Mask for typing +FF_CODE = idaapi.FF_CODE # Code ? +FF_DATA = idaapi.FF_DATA # Data ? +FF_TAIL = idaapi.FF_TAIL # Tail ? +FF_UNK = idaapi.FF_UNK # Unknown ? + +def isCode(F): return ((F & MS_CLS) == FF_CODE) # is code byte? +def isData(F): return ((F & MS_CLS) == FF_DATA) # is data byte? +def isTail(F): return ((F & MS_CLS) == FF_TAIL) # is tail byte? +def isUnknown(F): return ((F & MS_CLS) == FF_UNK) # is unexplored byte? +def isHead(F): return ((F & FF_DATA) != 0) # is start of code/data? + +# +# Common bits +# +MS_COMM = idaapi.MS_COMM # Mask of common bits +FF_COMM = idaapi.FF_COMM # Has comment? +FF_REF = idaapi.FF_REF # has references? +FF_LINE = idaapi.FF_LINE # Has next or prev cmt lines ? +FF_NAME = idaapi.FF_NAME # Has user-defined name ? +FF_LABL = idaapi.FF_LABL # Has dummy name? +FF_FLOW = idaapi.FF_FLOW # Exec flow from prev instruction? +FF_VAR = idaapi.FF_VAR # Is byte variable ? +FF_ANYNAME = FF_LABL | FF_NAME + +def isFlow(F): return ((F & FF_FLOW) != 0) +def isVar(F): return ((F & FF_VAR ) != 0) +def isExtra(F): return ((F & FF_LINE) != 0) +def isRef(F): return ((F & FF_REF) != 0) +def hasName(F): return ((F & FF_NAME) != 0) +def hasUserName(F): return ((F & FF_ANYNAME) == FF_NAME) + +MS_0TYPE = idaapi.MS_0TYPE # Mask for 1st arg typing +FF_0VOID = idaapi.FF_0VOID # Void (unknown)? +FF_0NUMH = idaapi.FF_0NUMH # Hexadecimal number? +FF_0NUMD = idaapi.FF_0NUMD # Decimal number? +FF_0CHAR = idaapi.FF_0CHAR # Char ('x')? +FF_0SEG = idaapi.FF_0SEG # Segment? +FF_0OFF = idaapi.FF_0OFF # Offset? +FF_0NUMB = idaapi.FF_0NUMB # Binary number? +FF_0NUMO = idaapi.FF_0NUMO # Octal number? +FF_0ENUM = idaapi.FF_0ENUM # Enumeration? +FF_0FOP = idaapi.FF_0FOP # Forced operand? +FF_0STRO = idaapi.FF_0STRO # Struct offset? +FF_0STK = idaapi.FF_0STK # Stack variable? + +MS_1TYPE = idaapi.MS_1TYPE # Mask for 2nd arg typing +FF_1VOID = idaapi.FF_1VOID # Void (unknown)? +FF_1NUMH = idaapi.FF_1NUMH # Hexadecimal number? +FF_1NUMD = idaapi.FF_1NUMD # Decimal number? +FF_1CHAR = idaapi.FF_1CHAR # Char ('x')? +FF_1SEG = idaapi.FF_1SEG # Segment? +FF_1OFF = idaapi.FF_1OFF # Offset? +FF_1NUMB = idaapi.FF_1NUMB # Binary number? +FF_1NUMO = idaapi.FF_1NUMO # Octal number? +FF_1ENUM = idaapi.FF_1ENUM # Enumeration? +FF_1FOP = idaapi.FF_1FOP # Forced operand? +FF_1STRO = idaapi.FF_1STRO # Struct offset? +FF_1STK = idaapi.FF_1STK # Stack variable? + +# The following macros answer questions like +# 'is the 1st (or 2nd) operand of instruction or data of the given type'? +# Please note that data items use only the 1st operand type (is...0) + +def isDefArg0(F): return ((F & MS_0TYPE) != FF_0VOID) +def isDefArg1(F): return ((F & MS_1TYPE) != FF_1VOID) +def isDec0(F): return ((F & MS_0TYPE) == FF_0NUMD) +def isDec1(F): return ((F & MS_1TYPE) == FF_1NUMD) +def isHex0(F): return ((F & MS_0TYPE) == FF_0NUMH) +def isHex1(F): return ((F & MS_1TYPE) == FF_1NUMH) +def isOct0(F): return ((F & MS_0TYPE) == FF_0NUMO) +def isOct1(F): return ((F & MS_1TYPE) == FF_1NUMO) +def isBin0(F): return ((F & MS_0TYPE) == FF_0NUMB) +def isBin1(F): return ((F & MS_1TYPE) == FF_1NUMB) +def isOff0(F): return ((F & MS_0TYPE) == FF_0OFF) +def isOff1(F): return ((F & MS_1TYPE) == FF_1OFF) +def isChar0(F): return ((F & MS_0TYPE) == FF_0CHAR) +def isChar1(F): return ((F & MS_1TYPE) == FF_1CHAR) +def isSeg0(F): return ((F & MS_0TYPE) == FF_0SEG) +def isSeg1(F): return ((F & MS_1TYPE) == FF_1SEG) +def isEnum0(F): return ((F & MS_0TYPE) == FF_0ENUM) +def isEnum1(F): return ((F & MS_1TYPE) == FF_1ENUM) +def isFop0(F): return ((F & MS_0TYPE) == FF_0FOP) +def isFop1(F): return ((F & MS_1TYPE) == FF_1FOP) +def isStroff0(F): return ((F & MS_0TYPE) == FF_0STRO) +def isStroff1(F): return ((F & MS_1TYPE) == FF_1STRO) +def isStkvar0(F): return ((F & MS_0TYPE) == FF_0STK) +def isStkvar1(F): return ((F & MS_1TYPE) == FF_1STK) + +# +# Bits for DATA bytes +# +DT_TYPE = idaapi.DT_TYPE & 0xFFFFFFFF # Mask for DATA typing + +FF_BYTE = idaapi.FF_BYTE & 0xFFFFFFFF # byte +FF_WORD = idaapi.FF_WORD & 0xFFFFFFFF # word +FF_DWRD = idaapi.FF_DWRD & 0xFFFFFFFF # dword +FF_QWRD = idaapi.FF_QWRD & 0xFFFFFFFF # qword +FF_TBYT = idaapi.FF_TBYT & 0xFFFFFFFF # tbyte +FF_ASCI = idaapi.FF_ASCI & 0xFFFFFFFF # ASCII ? +FF_STRU = idaapi.FF_STRU & 0xFFFFFFFF # Struct ? +FF_OWRD = idaapi.FF_OWRD & 0xFFFFFFFF # octaword (16 bytes) +FF_FLOAT = idaapi.FF_FLOAT & 0xFFFFFFFF # float +FF_DOUBLE = idaapi.FF_DOUBLE & 0xFFFFFFFF # double +FF_PACKREAL = idaapi.FF_PACKREAL & 0xFFFFFFFF # packed decimal real +FF_ALIGN = idaapi.FF_ALIGN & 0xFFFFFFFF # alignment directive + +def isByte(F): return (isData(F) and (F & DT_TYPE) == FF_BYTE) +def isWord(F): return (isData(F) and (F & DT_TYPE) == FF_WORD) +def isDwrd(F): return (isData(F) and (F & DT_TYPE) == FF_DWRD) +def isQwrd(F): return (isData(F) and (F & DT_TYPE) == FF_QWRD) +def isOwrd(F): return (isData(F) and (F & DT_TYPE) == FF_OWRD) +def isTbyt(F): return (isData(F) and (F & DT_TYPE) == FF_TBYT) +def isFloat(F): return (isData(F) and (F & DT_TYPE) == FF_FLOAT) +def isDouble(F): return (isData(F) and (F & DT_TYPE) == FF_DOUBLE) +def isPackReal(F): return (isData(F) and (F & DT_TYPE) == FF_PACKREAL) +def isASCII(F): return (isData(F) and (F & DT_TYPE) == FF_ASCI) +def isStruct(F): return (isData(F) and (F & DT_TYPE) == FF_STRU) +def isAlign(F): return (isData(F) and (F & DT_TYPE) == FF_ALIGN) + +# +# Bits for CODE bytes +# +MS_CODE = idaapi.MS_CODE & 0xFFFFFFFF +FF_FUNC = idaapi.FF_FUNC & 0xFFFFFFFF # function start? +FF_IMMD = idaapi.FF_IMMD & 0xFFFFFFFF # Has Immediate value ? +FF_JUMP = idaapi.FF_JUMP & 0xFFFFFFFF # Has jump table + +# +# Loader flags +# +NEF_SEGS = idaapi.NEF_SEGS # Create segments +NEF_RSCS = idaapi.NEF_RSCS # Load resources +NEF_NAME = idaapi.NEF_NAME # Rename entries +NEF_MAN = idaapi.NEF_MAN # Manual load +NEF_FILL = idaapi.NEF_FILL # Fill segment gaps +NEF_IMPS = idaapi.NEF_IMPS # Create imports section +NEF_FIRST = idaapi.NEF_FIRST # This is the first file loaded +NEF_CODE = idaapi.NEF_CODE # for load_binary_file: +NEF_RELOAD = idaapi.NEF_RELOAD # reload the file at the same place: +NEF_FLAT = idaapi.NEF_FLAT # Autocreated FLAT group (PE) + +# List of built-in functions +# -------------------------- +# +# The following conventions are used in this list: +# 'ea' is a linear address +# 'success' is 0 if a function failed, 1 otherwise +# 'void' means that function returns no meaningful value (always 0) +# +# All function parameter conversions are made automatically. +# +# ---------------------------------------------------------------------------- +# M I S C E L L A N E O U S +# ---------------------------------------------------------------------------- +def IsString(var): raise NotImplementedError, "this function is not needed in Python" +def IsLong(var): raise NotImplementedError, "this function is not needed in Python" +def IsFloat(var): raise NotImplementedError, "this function is not needed in Python" + +def MK_FP(seg, off): + """ + Return value of expression: ((seg<<4) + off) + """ + return (seg << 4) + off + +def form(format, *args): + raise DeprecatedIDCError, "form() is deprecated. Use python string operations instead." + +def substr(s, x1, x2): + raise DeprecatedIDCError, "substr() is deprecated. Use python string operations instead." + +def strstr(s1, s2): + raise DeprecatedIDCError, "strstr() is deprecated. Use python string operations instead." + +def strlen(s): + raise DeprecatedIDCError, "strlen() is deprecated. Use python string operations instead." + +def xtol(s): + raise DeprecatedIDCError, "xtol() is deprecated. Use python long() instead." + + +def atoa(ea): + """ + Convert address value to a string + Return address in the form 'seg000:1234' + (the same as in line prefixes) + + @param ea: address to format + """ + segname = SegName(ea) + + if segname == "": + segname = "0" + + return "%s:%X" % (segname, ea) + + +def ltoa(n, radix): + raise DeprecatedIDCError, "ltoa() is deprecated. Use python string operations instead." + +def atol(s): + raise DeprecatedIDCError, "atol() is deprecated. Use python long() instead." + + +def rotate_left(value, count, nbits, offset): + """ + Rotate a value to the left (or right) + + @param value: value to rotate + @param count: number of times to rotate. negative counter means + rotate to the right + @param nbits: number of bits to rotate + @param offset: offset of the first bit to rotate + + @return: the value with the specified field rotated + all other bits are not modified + """ + assert offset >= 0, "offset must be >= 0" + assert nbits > 0, "nbits must be > 0" + + mask = 2**(offset+nbits) - 2**offset + tmp = value & mask + + if count > 0: + for x in xrange(count): + if (tmp >> (offset+nbits-1)) & 1: + tmp = (tmp << 1) | (1 << offset) + else: + tmp = (tmp << 1) + else: + for x in xrange(-count): + if (tmp >> offset) & 1: + tmp = (tmp >> 1) | (1 << (offset+nbits-1)) + else: + tmp = (tmp >> 1) + + value = (value-(value&mask)) | (tmp & mask) + + return value + + +def rotate_dword(x, count): return rotate_left(x, count, 32, 0) +def rotate_word(x, count): return rotate_left(x, count, 16, 0) +def rotate_byte(x, count): return rotate_left(x, count, 8, 0) + + +# AddHotkey return codes +IDCHK_OK = 0 # ok +IDCHK_ARG = -1 # bad argument(s) +IDCHK_KEY = -2 # bad hotkey name +IDCHK_MAX = -3 # too many IDC hotkeys + +def AddHotkey(hotkey, idcfunc): + """ + Add hotkey for IDC function + + @param hotkey: hotkey name ('a', "Alt-A", etc) + @param idcfunc: IDC function name + + @note: GUI version doesn't support hotkeys + + @return: None + """ + return idaapi.add_idc_hotkey(hotkey, idcfunc) + + +def DelHotkey(hotkey): + """ + Delete IDC function hotkey + + @param hotkey: hotkey code to delete + """ + return idaapi.del_idc_hotkey(hotkey) + + +def Jump(ea): + """ + Move cursor to the specifed linear address + + @param ea: linear address + """ + return idaapi.jumpto(ea) + + +def Wait(): + """ + Process all entries in the autoanalysis queue + Wait for the end of autoanalysis + + @note: This function will suspend execution of the calling script + till the autoanalysis queue is empty. + """ + return idaapi.autoWait() + + +def CompileEx(input, isfile): + """ + Compile an IDC script + + The input should not contain functions that are + currently executing - otherwise the behaviour of the replaced + functions is undefined. + + @param input: if isfile != 0, then this is the name of file to compile + otherwise it holds the text to compile + @param isfile: specify if 'input' holds a filename or the expression itself + + @return: 0 - ok, otherwise it returns an error message + """ + if isfile: + res = idaapi.Compile(input) + else: + res = idaapi.CompileLine(input) + + if res: + return res + else: + return 0 + + +def Eval(expr): + """ + Evaluate an IDC expression + + @param expr: an expression + + @return: the expression value. If there are problems, the returned value will be "IDC_FAILURE: xxx" + where xxx is the error description + + @note: Python implementation evaluates IDC only, while IDC can call other registered languages + """ + rv = idaapi.idc_value_t() + + err = idaapi.calc_idc_expr(BADADDR, expr, rv) + if err: + return "IDC_FAILURE: "+err + else: + if rv.vtype == '\x01': # VT_STR + return rv.str + elif rv.vtype == '\x02': # long + return rv.num + elif rv.vtype == '\x07': # VT_STR2 + return rv.c_str() + else: + raise NotImplementedError, "Eval() supports only expressions returning strings or longs" + + +def EVAL_FAILURE(code): + """ + Check the result of Eval() for evaluation failures + + @param code: result of Eval() + + @return: True if there was an evaluation error + """ + return type(code) == types.StringType and code.startswith("IDC_FAILURE: ") + + +def SaveBase(idbname, flags=0): + """ + Save current database to the specified idb file + + @param idbname: name of the idb file. if empty, the current idb + file will be used. + @param flags: DBFL_BAK or 0 + """ + if len(idbname)==0: + idbname = idaapi.cvar.database_idb + saveflags = idaapi.cvar.database_flags + if flags & DBFL_BAK: + idaapi.cvar.database_flags |= DBFL_BAK + else: + idaapi.cvar.database_flags &= ~DBFL_BAK + res = idaapi.save_database(idbname, 0) + idaapi.cvar.database_flags = saveflags + return res + +DBFL_BAK = 0x04 # create backup file + + +def Exit(code): + """ + Stop execution of IDC program, close the database and exit to OS + + @param code: code to exit with. + + @return: - + """ + idaapi.qexit(code) + + +def Exec(command): + """ + Execute an OS command. + + @param command: command line to execute + + @return: error code from OS + + @note: + IDA will wait for the started program to finish. + In order to start the command in parallel, use OS methods. + For example, you may start another program in parallel using + "start" command. + """ + return os.system(command) + + +def Sleep(milliseconds): + """ + Sleep the specified number of milliseconds + This function suspends IDA for the specified amount of time + + @param milliseconds: time to sleep + """ + time.sleep(float(milliseconds)/1000) + + +def RunPlugin(name, arg): + """ + Load and run a plugin + + @param name: The plugin name is a short plugin name without an extension + @param arg: integer argument + + @return: 0 if could not load the plugin, 1 if ok + """ + return idaapi.load_and_run_plugin(name, arg) + + +def ApplySig(name): + """ + Load (plan to apply) a FLIRT signature file + + @param name: signature name without path and extension + + @return: 0 if could not load the signature file, !=0 otherwise + """ + return idaapi.plan_to_apply_idasgn(name) + + +#---------------------------------------------------------------------------- +# C H A N G E P R O G R A M R E P R E S E N T A T I O N +#---------------------------------------------------------------------------- + + +def DeleteAll(): + """ + Delete all segments, instructions, comments, i.e. everything + except values of bytes. + """ + ea = idaapi.cvar.inf.minEA + + # Brute-force nuke all info from all the heads + while ea != BADADDR and ea <= idaapi.cvar.inf.maxEA: + idaapi.del_local_name(ea) + idaapi.del_global_name(ea) + func = idaapi.get_func(ea) + if func: + idaapi.del_func_cmt(func, False) + idaapi.del_func_cmt(func, True) + idaapi.del_func(ea) + idaapi.del_hidden_area(ea) + seg = idaapi.getseg(ea) + if seg: + idaapi.del_segment_cmt(seg, False) + idaapi.del_segment_cmt(seg, True) + idaapi.del_segm(ea, idaapi.SEGDEL_KEEP | idaapi.SEGDEL_SILENT) + + ea = idaapi.next_head(ea, idaapi.cvar.inf.maxEA) + + +def MakeCode(ea): + """ + Create an instruction at the specified address + + @param ea: linear address + + @return: 0 - can not create an instruction (no such opcode, the instruction + would overlap with existing items, etc) otherwise returns length of the + instruction in bytes + """ + return idaapi.create_insn(ea) + + +def AnalyzeArea(sEA, eEA): + """ + Perform full analysis of the area + + @param sEA: starting linear address + @param eEA: ending linear address (excluded) + + @return: 1-ok, 0-Ctrl-Break was pressed. + """ + return idaapi.analyze_area(sEA, eEA) + + +def MakeNameEx(ea, name, flags): + """ + Rename an address + + @param ea: linear address + @param name: new name of address. If name == "", then delete old name + @param flags: combination of SN_... constants + + @return: 1-ok, 0-failure + """ + return idaapi.set_name(ea, name, flags) + +SN_CHECK = idaapi.SN_CHECK # Fail if the name contains invalid + # characters + # If this bit is clear, all invalid chars + # (those !is_ident_char()) will be replaced + # by SubstChar (usually '_') + # List of valid characters is defined in + # ida.cfg +SN_NOCHECK = idaapi.SN_NOCHECK # Replace invalid chars with SubstChar +SN_PUBLIC = idaapi.SN_PUBLIC # if set, make name public +SN_NON_PUBLIC = idaapi.SN_NON_PUBLIC # if set, make name non-public +SN_WEAK = idaapi.SN_WEAK # if set, make name weak +SN_NON_WEAK = idaapi.SN_NON_WEAK # if set, make name non-weak +SN_AUTO = idaapi.SN_AUTO # if set, make name autogenerated +SN_NON_AUTO = idaapi.SN_NON_AUTO # if set, make name non-autogenerated +SN_NOLIST = idaapi.SN_NOLIST # if set, exclude name from the list + # if not set, then include the name into + # the list (however, if other bits are set, + # the name might be immediately excluded + # from the list) +SN_NOWARN = idaapi.SN_NOWARN # don't display a warning if failed +SN_LOCAL = idaapi.SN_LOCAL # create local name. a function should exist. + # local names can't be public or weak. + # also they are not included into the list + # of names they can't have dummy prefixes + +def MakeComm(ea, comment): + """ + Set an indented regular comment of an item + + @param ea: linear address + @param comment: comment string + + @return: None + """ + return idaapi.set_cmt(ea, comment, 0) + + +def MakeRptCmt(ea, comment): + """ + Set an indented repeatable comment of an item + + @param ea: linear address + @param comment: comment string + + @return: None + """ + return idaapi.set_cmt(ea, comment, 1) + + +def MakeArray(ea, nitems): + """ + Create an array. + + @param ea: linear address + @param nitems: size of array in items + + @note: This function will create an array of the items with the same type as + the type of the item at 'ea'. If the byte at 'ea' is undefined, then + this function will create an array of bytes. + """ + flags = idaapi.getFlags(ea) + + if idaapi.isUnknown(flags): + flags = idaapi.FF_BYTE + + if idaapi.isStruct(flags): + ti = idaapi.opinfo_t() + assert idaapi.get_opinfo(ea, 0, flags, ti), "get_opinfo() failed" + itemsize = idaapi.get_data_elsize(ea, flags, ti) + tid = ti.tid + else: + itemsize = idaapi.get_item_size(ea) + tid = BADADDR + + return idaapi.do_data_ex(ea, flags, itemsize*nitems, tid) + + +def MakeStr(ea, endea): + """ + Create a string. + + This function creates a string (the string type is determined by the + value of GetLongPrm(INF_STRTYPE)) + + @param ea: linear address + @param endea: ending address of the string (excluded) + if endea == BADADDR, then length of string will be calculated + by the kernel + + @return: 1-ok, 0-failure + + @note: The type of an existing string is returned by GetStringType() + """ + return idaapi.make_ascii_string(ea, endea - ea, GetLongPrm(INF_STRTYPE)) + + +def MakeData(ea, flags, size, tid): + """ + Create a data item at the specified address + + @param ea: linear address + @param flags: FF_BYTE..FF_PACKREAL + @param size: size of item in bytes + @param tid: for FF_STRU the structure id + + @return: 1-ok, 0-failure + """ + return idaapi.do_data_ex(ea, flags, size, tid) + + +def MakeByte(ea): + """ + Convert the current item to a byte + + @param ea: linear address + + @return: 1-ok, 0-failure + """ + return idaapi.doByte(ea, 1) + + +def MakeWord(ea): + """ + Convert the current item to a word (2 bytes) + + @param ea: linear address + + @return: 1-ok, 0-failure + """ + return idaapi.doWord(ea, 2) + + +def MakeDword(ea): + """ + Convert the current item to a double word (4 bytes) + + @param ea: linear address + + @return: 1-ok, 0-failure + """ + return idaapi.doDwrd(ea, 4) + + +def MakeQword(ea): + """ + Convert the current item to a quadro word (8 bytes) + + @param ea: linear address + + @return: 1-ok, 0-failure + """ + return idaapi.doQwrd(ea, 8) + + +def MakeOword(ea): + """ + Convert the current item to a octa word (16 bytes) + + @param ea: linear address + + @return: 1-ok, 0-failure + """ + return idaapi.doOwrd(ea, 16) + + +def MakeFloat(ea): + """ + Convert the current item to a floating point (4 bytes) + + @param ea: linear address + + @return: 1-ok, 0-failure + """ + return idaapi.doFloat(ea, 4) + + +def MakeDouble(ea): + """ + Convert the current item to a double floating point (8 bytes) + + @param ea: linear address + + @return: 1-ok, 0-failure + """ + return idaapi.doDouble(ea, 8) + + +def MakePackReal(ea): + """ + Convert the current item to a packed real (10 or 12 bytes) + + @param ea: linear address + + @return: 1-ok, 0-failure + """ + return idaapi.doPackReal(ea, idaapi.ph_get_tbyte_size()) + + +def MakeTbyte(ea): + """ + Convert the current item to a tbyte (10 or 12 bytes) + + @param ea: linear address + + @return: 1-ok, 0-failure + """ + return idaapi.doTbyt(ea, idaapi.ph_get_tbyte_size()) + + +def MakeStructEx(ea, size, strname): + """ + Convert the current item to a structure instance + + @param ea: linear address + @param size: structure size in bytes. -1 means that the size + will be calculated automatically + @param strname: name of a structure type + + @return: 1-ok, 0-failure + """ + strid = idaapi.get_struc_id(strname) + + if size == -1: + size = idaapi.get_struc_size(strid) + + return idaapi.doStruct(ea, size, strid) + + +def MakeAlign(ea, count, align): + """ + Convert the current item to an alignment directive + + @param ea: linear address + @param count: number of bytes to convert + @param align: 0 or 1..32 + if it is 0, the correct alignment will be calculated + by the kernel + + @return: 1-ok, 0-failure + """ + return idaapi.doAlign(ea, count, align) + + +def MakeLocal(start, end, location, name): + """ + Create a local variable + + @param start: start of address range for the local variable + @param end: end of address range for the local variable + @param location: the variable location in the "[bp+xx]" form where xx is + a number. The location can also be specified as a + register name. + @param name: name of the local variable + + @return: 1-ok, 0-failure + + @note: For the stack variables the end address is ignored. + If there is no function at 'start' then this function. + will fail. + """ + func = idaapi.get_func(start) + + if not func: + return 0 + + # Find out if location is in the [bp+xx] form + r = re.compile("\[([a-z]+)([-+][0-9a-fx]+)", re.IGNORECASE) + m = r.match(location) + + if m: + # Location in the form of [bp+xx] + register = idaapi.str2reg(m.group(1)) + offset = int(m.group(2), 0) + frame = idaapi.get_frame(func) + + if register == -1 or not frame: + return 0 + + offset += func.frsize + member = idaapi.get_member(frame, offset) + + if member: + # Member already exists, rename it + if idaapi.set_member_name(frame, offset, name): + return 1 + else: + return 0 + else: + # No member at the offset, create a new one + if idaapi.add_struc_member(frame, + name, + offset, + idaapi.byteflag(), + None, 1) == 0: + return 1 + else: + return 0 + else: + # Location as simple register name + return idaapi.add_regvar(func, start, end, location, name, None) + + +def MakeUnkn(ea, flags): + """ + Convert the current item to an explored item + + @param ea: linear address + @param flags: combination of DOUNK_* constants + + @return: None + """ + return idaapi.do_unknown(ea, flags) + + +def MakeUnknown(ea, size, flags): + """ + Convert the current item to an explored item + + @param ea: linear address + @param size: size of the range to undefine (for MakeUnknown) + @param flags: combination of DOUNK_* constants + + @return: None + """ + return idaapi.do_unknown_range(ea, size, flags) + + +DOUNK_SIMPLE = idaapi.DOUNK_SIMPLE # simply undefine the specified item +DOUNK_EXPAND = idaapi.DOUNK_EXPAND # propogate undefined items, for example + # if removing an instruction removes all + # references to the next instruction, then + # plan to convert to unexplored the next + # instruction too. +DOUNK_DELNAMES = idaapi.DOUNK_DELNAMES # delete any names at the specified address(es) + + +def SetArrayFormat(ea, flags, litems, align): + """ + Set array representation format + + @param ea: linear address + @param flags: combination of AP_... constants or 0 + @param litems: number of items per line. 0 means auto + @param align: element alignment + - -1: do not align + - 0: automatic alignment + - other values: element width + + @return: 1-ok, 0-failure + """ + return Eval("SetArrayFormat(0x%X, 0x%X, %d, %d)"%(ea, flags, litems, align)) + +AP_ALLOWDUPS = 0x00000001L # use 'dup' construct +AP_SIGNED = 0x00000002L # treats numbers as signed +AP_INDEX = 0x00000004L # display array element indexes as comments +AP_ARRAY = 0x00000008L # reserved (this flag is not stored in database) +AP_IDXBASEMASK = 0x000000F0L # mask for number base of the indexes +AP_IDXDEC = 0x00000000L # display indexes in decimal +AP_IDXHEX = 0x00000010L # display indexes in hex +AP_IDXOCT = 0x00000020L # display indexes in octal +AP_IDXBIN = 0x00000030L # display indexes in binary + +def OpBinary(ea, n): + """ + Convert an operand of the item (instruction or data) to a binary number + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + + @return: 1-ok, 0-failure + + @note: the data items use only the type of the first operand + """ + return idaapi.op_bin(ea, n) + + +def OpOctal(ea, n): + """ + Convert an operand of the item (instruction or data) to an octal number + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + """ + return idaapi.op_oct(ea, n) + + +def OpDecimal(ea, n): + """ + Convert an operand of the item (instruction or data) to a decimal number + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + """ + return idaapi.op_dec(ea, n) + + +def OpHex(ea, n): + """ + Convert an operand of the item (instruction or data) to a hexadecimal number + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + """ + return idaapi.op_hex(ea, n) + + +def OpChr(ea, n): + """ + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + """ + return idaapi.op_chr(ea, n) + + +def OpOff(ea, n, base): + """ + Convert operand to an offset + (for the explanations of 'ea' and 'n' please see OpBinary()) + + Example: + ======== + + seg000:2000 dw 1234h + + and there is a segment at paragraph 0x1000 and there is a data item + within the segment at 0x1234: + + seg000:1234 MyString db 'Hello, world!',0 + + Then you need to specify a linear address of the segment base to + create a proper offset: + + OpOff(["seg000",0x2000],0,0x10000); + + and you will have: + + seg000:2000 dw offset MyString + + Motorola 680x0 processor have a concept of "outer offsets". + If you want to create an outer offset, you need to combine number + of the operand with the following bit: + + Please note that the outer offsets are meaningful only for + Motorola 680x0. + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + @param base: base of the offset as a linear address + If base == BADADDR then the current operand becomes non-offset + """ + return idaapi.set_offset(ea, n, base) + + +OPND_OUTER = idaapi.OPND_OUTER # outer offset base + + +def OpOffEx(ea, n, reftype, target, base, tdelta): + """ + Convert operand to a complex offset expression + This is a more powerful version of OpOff() function. + It allows to explicitly specify the reference type (off8,off16, etc) + and the expression target with a possible target delta. + The complex expressions are represented by IDA in the following form: + + target + tdelta - base + + If the target is not present, then it will be calculated using + + target = operand_value - tdelta + base + + The target must be present for LOW.. and HIGH.. reference types + + @param ea: linear address of the instruction/data + @param n: number of operand to convert (the same as in OpOff) + @param reftype: one of REF_... constants + @param target: an explicitly specified expression target. if you don't + want to specify it, use -1. Please note that LOW... and + HIGH... reference type requre the target. + @param base: the offset base (a linear address) + @param tdelta: a displacement from the target which will be displayed + in the expression. + + @return: success (boolean) + """ + return idaapi.op_offset(ea, n, reftype, target, base, tdelta) + + +REF_OFF8 = idaapi.REF_OFF8 # 8bit full offset +REF_OFF16 = idaapi.REF_OFF16 # 16bit full offset +REF_OFF32 = idaapi.REF_OFF32 # 32bit full offset +REF_LOW8 = idaapi.REF_LOW8 # low 8bits of 16bit offset +REF_LOW16 = idaapi.REF_LOW16 # low 16bits of 32bit offset +REF_HIGH8 = idaapi.REF_HIGH8 # high 8bits of 16bit offset +REF_HIGH16 = idaapi.REF_HIGH16 # high 16bits of 32bit offset +REF_VHIGH = idaapi.REF_VHIGH # high ph.high_fixup_bits of 32bit offset (processor dependent) +REF_VLOW = idaapi.REF_VLOW # low (32-ph.high_fixup_bits) of 32bit offset (processor dependent) +REF_OFF64 = idaapi.REF_OFF64 # 64bit full offset +REFINFO_RVA = 0x10 # based reference (rva) +REFINFO_PASTEND = 0x20 # reference past an item it may point to an nonexistitng + # do not destroy alignment dirs +REFINFO_NOBASE = 0x80 # offset base is a number + # that base have be any value + # nb: base xrefs are created only if base + # points to the middle of a segment + + +def OpSeg(ea, n): + """ + Convert operand to a segment expression + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + """ + return idaapi.op_seg(ea, n) + + +def OpNumber(ea, n): + """ + Convert operand to a number (with default number base, radix) + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + """ + return idaapi.op_num(ea, n) + + +def OpFloat(ea, n): + """ + Convert operand to a floating-point number + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + + @return: 1-ok, 0-failure + """ + return idaapi.op_flt(ea, n) + + +def OpAlt(ea, n, opstr): + """ + Specify operand represenation manually. + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + @param opstr: a string represenation of the operand + + @note: IDA will not check the specified operand, it will simply display + it instead of the orginal representation of the operand. + """ + return idaapi.set_forced_operand(ea, n, opstr) + + +def OpSign(ea, n): + """ + Change sign of the operand + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + """ + return idaapi.toggle_sign(ea, n) + + +def OpNot(ea, n): + """ + Toggle the bitwise not operator for the operand + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + """ + idaapi.toggle_bnot(ea, n) + return True + + +def OpEnumEx(ea, n, enumid, serial): + """ + Convert operand to a symbolic constant + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + @param enumid: id of enumeration type + @param serial: serial number of the constant in the enumeration + The serial numbers are used if there are more than + one symbolic constant with the same value in the + enumeration. In this case the first defined constant + get the serial number 0, then second 1, etc. + There could be 256 symbolic constants with the same + value in the enumeration. + """ + return idaapi.op_enum(ea, n, enumid, serial) + + +def OpStroffEx(ea, n, strid, delta): + """ + Convert operand to an offset in a structure + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + @param strid: id of a structure type + @param delta: struct offset delta. usually 0. denotes the difference + between the structure base and the pointer into the structure. + + """ + path = idaapi.tid_array(1) + path[0] = strid + return idaapi.op_stroff(ea, n, path.cast(), 1, delta) + + +def OpStkvar(ea, n): + """ + Convert operand to a stack variable + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + """ + return idaapi.op_stkvar(ea, n) + + +def OpHigh(ea, n, target): + """ + Convert operand to a high offset + High offset is the upper 16bits of an offset. + This type is used by TMS320C6 processors (and probably by other + RISC processors too) + + @param ea: linear address + @param n: number of operand + - 0 - the first operand + - 1 - the second, third and all other operands + - -1 - all operands + @param target: the full value (all 32bits) of the offset + """ + return idaapi.op_offset(ea, n, idaapi.REF_HIGH16, target) + + +def MakeVar(ea): + """ + Mark the location as "variable" + + @param ea: address to mark + + @return: None + + @note: All that IDA does is to mark the location as "variable". + Nothing else, no additional analysis is performed. + This function may disappear in the future. + """ + idaapi.doVar(ea, 1) + + +def ExtLinA(ea, n, line): + """ + Specify an additional line to display before the generated ones. + + @param ea: linear address + @param n: number of anterior additioal line (0..MAX_ITEM_LINES) + @param line: the line to display + + @return: None + + @note: IDA displays additional lines from number 0 up to the first unexisting + additional line. So, if you specify additional line #150 and there is no + additional line #149, your line will not be displayed. MAX_ITEM_LINES is + defined in IDA.CFG + """ + idaapi.ExtraUpdate(ea, line, idaapi.E_PREV + n) + + +def ExtLinB(ea, n, line): + """ + Specify an additional line to display after the generated ones. + + @param ea: linear address + @param n: number of posterior additioal line (0..MAX_ITEM_LINES) + @param line: the line to display + + @return: None + + @note: IDA displays additional lines from number 0 up to the first + unexisting additional line. So, if you specify additional line #150 + and there is no additional line #149, your line will not be displayed. + MAX_ITEM_LINES is defined in IDA.CFG + """ + idaapi.ExtraUpdate(ea, line, idaapi.E_NEXT + n) + + +def DelExtLnA(ea, n): + """ + Delete an additional anterior line + + @param ea: linear address + @param n: number of anterior additioal line (0..500) + + @return: None + """ + idaapi.ExtraDel(ea, idaapi.E_PREV + n) + + +def DelExtLnB(ea, n): + """ + Delete an additional posterior line + + @param ea: linear address + @param n: number of posterior additioal line (0..500) + + @return: None + """ + idaapi.ExtraDel(ea, idaapi.E_NEXT + n) + + +def SetManualInsn(ea, insn): + """ + Specify instruction represenation manually. + + @param ea: linear address + @param insn: a string represenation of the operand + + @note: IDA will not check the specified instruction, it will simply + display it instead of the orginal representation. + """ + return idaapi.set_manual_insn(ea, insn) + + +def GetManualInsn(ea): + """ + Get manual representation of instruction + + @param ea: linear address + + @note: This function returns value set by SetManualInsn earlier. + """ + return idaapi.get_manual_insn(ea) + + +def PatchDbgByte(ea,value): + """ + Change a byte in the debugged process memory only + + @param ea: address + @param value: new value of the byte + + @return: 1 if successful, 0 if not + """ + return idaapi.put_dbg_byte(ea, value) + + +def PatchByte(ea, value): + """ + Change value of a program byte + If debugger was active then the debugged process memory will be patched too + + @param ea: linear address + @param value: new value of the byte + + @return: 1 if successful, 0 if not + """ + return idaapi.patch_byte(ea, value) + + +def PatchWord(ea, value): + """ + Change value of a program word (2 bytes) + + @param ea: linear address + @param value: new value of the word + + @return: 1 if successful, 0 if not + """ + return idaapi.patch_word(ea, value) + + +def PatchDword(ea, value): + """ + Change value of a double word + + @param ea: linear address + @param value: new value of the double word + + @return: 1 if successful, 0 if not + """ + return idaapi.patch_long(ea, value) + + +def SetFlags(ea, flags): + """ + Set new value of flags + This function should not used be used directly if possible. + It changes properties of a program byte and if misused, may lead to + very-very strange results. + + @param ea: adress + @param flags: new flags value + """ + return idaapi.setFlags(ea, flags) + +def SetRegEx(ea, reg, value, tag): + """ + Set value of a segment register. + + @param ea: linear address + @param reg: name of a register, like "cs", "ds", "es", etc. + @param value: new value of the segment register. + @param tag: of SR_... constants + + @note: IDA keeps tracks of all the points where segment register change their + values. This function allows you to specify the correct value of a segment + register if IDA is not able to find the corrent value. + + See also SetReg() compatibility macro. + """ + reg = idaapi.str2reg(reg); + if reg >= 0: + return idaapi.splitSRarea1(ea, reg, value, tag) + else: + return False + +SR_inherit = 1 # value is inherited from the previous area +SR_user = 2 # value is specified by the user +SR_auto = 3 # value is determined by IDA +SR_autostart = 4 # as SR_auto for segment starting address + + +def AutoMark2(start, end, queuetype): + """ + Plan to perform an action in the future. + This function will put your request to a special autoanalysis queue. + Later IDA will retrieve the request from the queue and process + it. There are several autoanalysis queue types. IDA will process all + queries from the first queue and then switch to the second queue, etc. + """ + return idaapi.auto_mark_range(start, end, queuetype) + + +def AutoUnmark(start, end, queuetype): + """ + Remove range of addresses from a queue. + """ + return idaapi.autoUnmark(start, end, queuetype) + + +def AutoMark(ea,qtype): + """ + Plan to analyze an address + """ + return AutoMark2(ea,ea+1,qtype) + +AU_UNK = idaapi.AU_UNK # make unknown +AU_CODE = idaapi.AU_CODE # convert to instruction +AU_PROC = idaapi.AU_PROC # make function +AU_USED = idaapi.AU_USED # reanalyze +AU_LIBF = idaapi.AU_LIBF # apply a flirt signature (the current signature!) +AU_FINAL = idaapi.AU_FINAL # coagulate unexplored items + + +#---------------------------------------------------------------------------- +# P R O D U C E O U T P U T F I L E S +#---------------------------------------------------------------------------- + +def GenerateFile(filetype, path, ea1, ea2, flags): + """ + Generate an output file + + @param filetype: type of output file. One of OFILE_... symbols. See below. + @param path: the output file path (will be overwritten!) + @param ea1: start address. For some file types this argument is ignored + @param ea2: end address. For some file types this argument is ignored + @param flags: bit combination of GENFLG_... + + @returns: number of the generated lines. + -1 if an error occured + OFILE_EXE: 0-can't generate exe file, 1-ok + """ + f = idaapi.fopenWT(path) + + if f: + retval = idaapi.gen_file(filetype, f, ea1, ea2, flags) + idaapi.eclose(f) + return retval + else: + return -1 + + +# output file types: +OFILE_MAP = idaapi.OFILE_MAP +OFILE_EXE = idaapi.OFILE_EXE +OFILE_IDC = idaapi.OFILE_IDC +OFILE_LST = idaapi.OFILE_LST +OFILE_ASM = idaapi.OFILE_ASM +OFILE_DIF = idaapi.OFILE_DIF + +# output control flags: +GENFLG_MAPSEG = idaapi.GENFLG_MAPSEG # map: generate map of segments +GENFLG_MAPNAME = idaapi.GENFLG_MAPNAME # map: include dummy names +GENFLG_MAPDMNG = idaapi.GENFLG_MAPDMNG # map: demangle names +GENFLG_MAPLOC = idaapi.GENFLG_MAPLOC # map: include local names +GENFLG_IDCTYPE = idaapi.GENFLG_IDCTYPE # idc: gen only information about types +GENFLG_ASMTYPE = idaapi.GENFLG_ASMTYPE # asm&lst: gen information about types too +GENFLG_GENHTML = idaapi.GENFLG_GENHTML # asm&lst: generate html (gui version only) +GENFLG_ASMINC = idaapi.GENFLG_ASMINC # asm&lst: gen information only about types + +def GenFuncGdl(outfile, title, ea1, ea2, flags): + """ + Generate a flow chart GDL file + + @param outfile: output file name. GDL extension will be used + @param title: graph title + @param ea1: beginning of the area to flow chart + @param ea2: end of the area to flow chart. + @param flags: combination of CHART_... constants + + @note: If ea2 == BADADDR then ea1 is treated as an address within a function. + That function will be flow charted. + """ + return idaapi.gen_flow_graph(outfile, title, None, ea1, ea2, flags) + + +CHART_PRINT_NAMES = 0x1000 # print labels for each block? +CHART_GEN_GDL = 0x4000 # generate .gdl file (file extension is forced to .gdl) +CHART_WINGRAPH = 0x8000 # call wingraph32 to display the graph +CHART_NOLIBFUNCS = 0x0400 # don't include library functions in the graph + + +def GenCallGdl(outfile, title, flags): + """ + Generate a function call graph GDL file + + @param outfile: output file name. GDL extension will be used + @param title: graph title + @param flags: combination of CHART_GEN_GDL, CHART_WINGRAPH, CHART_NOLIBFUNCS + """ + return idaapi.gen_simple_call_chart(outfile, "Generating chart", title, flags) + + +#---------------------------------------------------------------------------- +# C O M M O N I N F O R M A T I O N +#---------------------------------------------------------------------------- +def GetIdaDirectory (): + """ + Get IDA directory + + This function returns the directory where IDA.EXE resides + """ + return idaapi.idadir() + + +def GetInputFile(): + """ + Get input file name + + This function returns name of the file being disassembled + """ + return idaapi.get_root_filename() + + +def GetInputFilePath(): + """ + Get input file path + + This function returns the full path of the file being disassembled + """ + return idaapi.get_input_file_path() + + +def SetInputFilePath(path): + """ + Set input file name + This function updates the file name that is stored in the database + It is used by the debugger and other parts of IDA + Use it when the database is moved to another location or when you + use remote debugging. + + @param path: new input file path + """ + return idaapi.set_root_filename(path) + + +def GetIdbPath(): + """ + Get IDB full path + + This function returns full path of the current IDB database + """ + return idaapi.cvar.database_idb + + +def GetInputMD5(): + """ + Return the MD5 hash of the input binary file + + @return: MD5 string or None on error + """ + ua=idaapi.uchar_array(16) + if idaapi.retrieve_input_file_md5(ua.cast()): + md5str="" + for i in range(16): + md5str += "%02x" % ua[i] + return md5str + else: + return None + + +def GetFlags(ea): + """ + Get internal flags + + @param ea: linear address + + @return: 32-bit value of internal flags. See start of IDC.IDC file + for explanations. + """ + return idaapi.getFlags(ea) + + +def IdbByte(ea): + """ + Get one byte (8-bit) of the program at 'ea' from the database even if the debugger is active + + @param ea: linear address + + @return: byte value. If the byte has no value then 0xFF is returned. + + @note: If the current byte size is different from 8 bits, then the returned value may have more 1's. + To check if a byte has a value, use this expr: hasValue(GetFlags(ea)) + """ + return idaapi.get_db_byte(ea) + + +def Byte(ea): + """ + Get value of program byte + + @param ea: linear address + + @return: value of byte. If byte has no value then returns 0xFF + If the current byte size is different from 8 bits, then the returned value + might have more 1's. + To check if a byte has a value, use functions hasValue(GetFlags(ea)) + """ + return idaapi.get_byte(ea) + + +def __DbgValue(ea, len): + if len not in idaapi.__struct_unpack_table: + return None + r = idaapi.dbg_read_memory(ea, len) + return None if r is None else struct.unpack((">" if idaapi.cvar.inf.mf else "<") + idaapi.__struct_unpack_table[len][1], r)[0] + + +def DbgByte(ea): + """ + Get value of program byte using the debugger memory + + @param ea: linear address + @return: The value or None on failure. + """ + return __DbgValue(ea, 1) + + +def DbgWord(ea): + """ + Get value of program word using the debugger memory + + @param ea: linear address + @return: The value or None on failure. + """ + return __DbgValue(ea, 2) + + +def DbgDword(ea): + """ + Get value of program double-word using the debugger memory + + @param ea: linear address + @return: The value or None on failure. + """ + return __DbgValue(ea, 4) + + +def DbgQword(ea): + """ + Get value of program quadro-word using the debugger memory + + @param ea: linear address + @return: The value or None on failure. + """ + return __DbgValue(ea, 8) + + +def GetOriginalByte(ea): + """ + Get original value of program byte + + @param ea: linear address + + @return: the original value of byte before any patch applied to it + """ + return idaapi.get_original_byte(ea) + + +def Word(ea): + """ + Get value of program word (2 bytes) + + @param ea: linear address + + @return: the value of the word. If word has no value then returns 0xFFFF + If the current byte size is different from 8 bits, then the returned value + might have more 1's. + """ + return idaapi.get_word(ea) + + +def Dword(ea): + """ + Get value of program double word (4 bytes) + + @param ea: linear address + + @return: the value of the double word. If failed returns -1 + """ + return idaapi.get_long(ea) + + +def Qword(ea): + """ + Get value of program quadro word (8 bytes) + + @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" + + +def GetFloat(ea): + """ + Get value of a floating point number (4 bytes) + + @param ea: linear address + + @return: float + """ + tmp = idaapi.get_many_bytes(ea, 4) + return struct.unpack("f", tmp)[0] + + +def GetDouble(ea): + """ + Get value of a floating point number (8 bytes) + + @param ea: linear address + + @return: double + """ + tmp = idaapi.get_many_bytes(ea, 8) + return struct.unpack("d", tmp)[0] + + +def LocByName(name): + """ + Get linear address of a name + + @param name: name of program byte + + @return: address of the name + badaddr - no such name + """ + return idaapi.get_name_ea(BADADDR, name) + + +def LocByNameEx(fromaddr, name): + """ + Get linear address of a name + + @param fromaddr: the referring address. Allows to retrieve local label + addresses in functions. If a local name is not found, + then address of a global name is returned. + + @param name: name of program byte + + @return: address of the name (BADADDR - no such name) + + @note: Dummy names (like byte_xxxx where xxxx are hex digits) are parsed by this + function to obtain the address. The database is not consulted for them. + """ + return idaapi.get_name_ea(fromaddr, name) + + +def SegByBase(base): + """ + Get segment by segment base + + @param base: segment base paragraph or selector + + @return: linear address of the start of the segment or BADADDR + if no such segment + """ + sel = idaapi.find_selector(base) + seg = idaapi.get_segm_by_sel(sel) + + if seg: + return seg.startEA + else: + return BADADDR + + +def ScreenEA(): + """ + Get linear address of cursor + """ + return idaapi.get_screen_ea() + + +def GetCurrentLine(): + """ + Get the disassembly line at the cursor + + @return: string + """ + return idaapi.tag_remove(idaapi.get_curline()) + + +def SelStart(): + """ + Get start address of the selected area + returns BADADDR - the user has not selected an area + """ + selection, startaddr, endaddr = idaapi.read_selection() + + if selection == 1: + return startaddr + else: + return BADADDR + + +def SelEnd(): + """ + Get end address of the selected area + + @return: BADADDR - the user has not selected an area + """ + selection, startaddr, endaddr = idaapi.read_selection() + + if selection == 1: + return endaddr + else: + return BADADDR + + +def GetReg(ea, reg): + """ + Get value of segment register at the specified address + + @param ea: linear address + @param reg: name of segment register + + @return: the value of the segment register or -1 on error + + @note: The segment registers in 32bit program usually contain selectors, + so to get paragraph pointed by the segment register you need to + call AskSelector() function. + """ + reg = idaapi.str2reg(reg); + if reg >= 0: + return idaapi.getSR(ea, reg) + else: + return -1 + +def NextAddr(ea): + """ + Get next address in the program + + @param ea: linear address + + @return: BADADDR - the specified address in the last used address + """ + return idaapi.nextaddr(ea) + + +def PrevAddr(ea): + """ + Get previous address in the program + + @param ea: linear address + + @return: BADADDR - the specified address in the first address + """ + return idaapi.prevaddr(ea) + + +def NextHead(ea, maxea): + """ + Get next defined item (instruction or data) in the program + + @param ea: linear address to start search from + @param maxea: the search will stop at the address + maxea is not included in the search range + + @return: BADADDR - no (more) defined items + """ + return idaapi.next_head(ea, maxea) + + +def PrevHead(ea, minea): + """ + Get previous defined item (instruction or data) in the program + + @param ea: linear address to start search from + @param minea: the search will stop at the address + minea is included in the search range + + @return: BADADDR - no (more) defined items + """ + return idaapi.prev_head(ea, minea) + + +def NextNotTail(ea): + """ + Get next not-tail address in the program + This function searches for the next displayable address in the program. + The tail bytes of instructions and data are not displayable. + + @param ea: linear address + + @return: BADADDR - no (more) not-tail addresses + """ + return idaapi.next_not_tail(ea) + + +def PrevNotTail(ea): + """ + Get previous not-tail address in the program + This function searches for the previous displayable address in the program. + The tail bytes of instructions and data are not displayable. + + @param ea: linear address + + @return: BADADDR - no (more) not-tail addresses + """ + return idaapi.prev_not_tail(ea) + + +def ItemEnd(ea): + """ + Get address of the end of the item (instruction or data) + + @param ea: linear address + + @return: address past end of the item at 'ea' + """ + return idaapi.get_item_end(ea) + + +def ItemSize(ea): + """ + Get size of instruction or data item in bytes + + @param ea: linear address + + @return: 1..n + """ + return idaapi.get_item_end(ea) - ea + + +def NameEx(fromaddr, ea): + """ + Get visible name of program byte + + This function returns name of byte as it is displayed on the screen. + If a name contains illegal characters, IDA replaces them by the + substitution character during displaying. See IDA.CFG for the + definition of the substitution character. + + @param fromaddr: the referring address. May be BADADDR. + Allows to retrieve local label addresses in functions. + If a local name is not found, then a global name is + returned. + @param ea: linear address + + @return: "" - byte has no name + """ + name = idaapi.get_name(fromaddr, ea) + + if not name: + return "" + else: + return name + + +def GetTrueNameEx(fromaddr, ea): + """ + Get true name of program byte + + This function returns name of byte as is without any replacements. + + @param fromaddr: the referring address. May be BADADDR. + Allows to retrieve local label addresses in functions. + If a local name is not found, then a global name is returned. + @param ea: linear address + + @return: "" - byte has no name + """ + name = idaapi.get_true_name(fromaddr, ea) + + if not name: + return "" + else: + return name + + +def Demangle(name, disable_mask): + """ + Demangle a name + + @param name: name to demangle + @param disable_mask: a mask that tells how to demangle the name + it is a good idea to get this mask using + GetLongPrm(INF_SHORT_DN) or GetLongPrm(INF_LONG_DN) + + @return: a demangled name + If the input name cannot be demangled, returns None + """ + return idaapi.demangle_name(name, disable_mask) + + +def GetDisasm(ea): + """ + Get disassembly line + + @param ea: linear address of instruction + + @return: "" - no instruction at the specified location + + @note: this function may not return exactly the same mnemonics + as you see on the screen. + """ + text = idaapi.generate_disasm_line(ea) + if text: + return idaapi.tag_remove(text) + else: + return "" + + +def GetMnem(ea): + """ + Get instruction mnemonics + + @param ea: linear address of instruction + + @return: "" - no instruction at the specified location + + @note: this function may not return exactly the same mnemonics + as you see on the screen. + """ + res = idaapi.ua_mnem(ea) + + if not res: + return "" + else: + return res + + +def GetOpnd(ea, n): + """ + Get operand of an instruction + + @param ea: linear address of instruction + @param n: number of operand: + 0 - the first operand + 1 - the second operand + + @return: the current text representation of operand + """ + res = idaapi.ua_outop2(ea, n) + + if not res: + return "" + else: + return idaapi.tag_remove(res) + + +def GetOpType(ea, n): + """ + Get type of instruction operand + + @param ea: linear address of instruction + @param n: number of operand: + 0 - the first operand + 1 - the second operand + + @return: any of o_* constants or -1 on error + """ + inslen = idaapi.decode_insn(ea) + return -1 if inslen == 0 else idaapi.cmd.Operands[n].type + + +o_void = idaapi.o_void # No Operand ---------- +o_reg = idaapi.o_reg # General Register (al,ax,es,ds...) reg +o_mem = idaapi.o_mem # Direct Memory Reference (DATA) addr +o_phrase = idaapi.o_phrase # Memory Ref [Base Reg + Index Reg] phrase +o_displ = idaapi.o_displ # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr +o_imm = idaapi.o_imm # Immediate Value value +o_far = idaapi.o_far # Immediate Far Address (CODE) addr +o_near = idaapi.o_near # Immediate Near Address (CODE) addr +o_idpspec0 = idaapi.o_idpspec0 # IDP specific type +o_idpspec1 = idaapi.o_idpspec1 # IDP specific type +o_idpspec2 = idaapi.o_idpspec2 # IDP specific type +o_idpspec3 = idaapi.o_idpspec3 # IDP specific type +o_idpspec4 = idaapi.o_idpspec4 # IDP specific type +o_idpspec5 = idaapi.o_idpspec5 # IDP specific type +o_last = idaapi.o_last # first unused type + +# x86 +o_trreg = idaapi.o_idpspec0 # trace register +o_dbreg = idaapi.o_idpspec1 # debug register +o_crreg = idaapi.o_idpspec2 # control register +o_fpreg = idaapi.o_idpspec3 # floating point register +o_mmxreg = idaapi.o_idpspec4 # mmx register +o_xmmreg = idaapi.o_idpspec5 # xmm register + +# arm +o_reglist = idaapi.o_idpspec1 # Register list (for LDM/STM) +o_creglist = idaapi.o_idpspec2 # Coprocessor register list (for CDP) +o_creg = idaapi.o_idpspec3 # Coprocessor register (for LDC/STC) +o_fpreg = idaapi.o_idpspec4 # Floating point register +o_fpreglist = idaapi.o_idpspec5 # Floating point register list +o_text = (idaapi.o_idpspec5+1) # Arbitrary text stored in the operand + +# ppc +o_spr = idaapi.o_idpspec0 # Special purpose register +o_twofpr = idaapi.o_idpspec1 # Two FPRs +o_shmbme = idaapi.o_idpspec2 # SH & MB & ME +o_crf = idaapi.o_idpspec3 # crfield x.reg +o_crb = idaapi.o_idpspec4 # crbit x.reg +o_dcr = idaapi.o_idpspec5 # Device control register + +def GetOperandValue(ea, n): + """ + Get number used in the operand + + This function returns an immediate number used in the operand + + @param ea: linear address of instruction + @param n: the operand number + + @return: value + operand is an immediate value => immediate value + operand has a displacement => displacement + operand is a direct memory ref => memory address + operand is a register => register number + operand is a register phrase => phrase number + otherwise => -1 + """ + inslen = idaapi.decode_insn(ea) + if inslen == 0: + return -1 + op = idaapi.cmd.Operands[n] + if not op: + return -1 + + if op.type in [ idaapi.o_mem, idaapi.o_far, idaapi.o_near, idaapi.o_displ ]: + value = op.addr + elif op.type == idaapi.o_reg: + value = op.reg + elif op.type == idaapi.o_imm: + value = op.value + elif op.type == idaapi.o_phrase: + value = op.phrase + else: + value = -1 + return value + + +def LineA(ea, num): + """ + Get anterior line + + @param ea: linear address + @param num: number of anterior line (0..MAX_ITEM_LINES) + MAX_ITEM_LINES is defined in IDA.CFG + + @return: anterior line string + """ + return idaapi.ExtraGet(ea, idaapi.E_PREV + num) + + +def LineB(ea, num): + """ + Get posterior line + + @param ea: linear address + @param num: number of posterior line (0..MAX_ITEM_LINES) + + @return: posterior line string + """ + return idaapi.ExtraGet(ea, idaapi.E_NEXT + num) + + +def GetCommentEx(ea, repeatable): + """ + Get regular indented comment + + @param ea: linear address + + @return: string or None if it fails + """ + return idaapi.get_cmt(ea, repeatable) + + +def CommentEx(ea, repeatable): GetCommentEx(ea, repeatable) + + +def AltOp(ea, n): + """ + Get manually entered operand string + + @param ea: linear address + @param n: number of operand: + 0 - the first operand + 1 - the second operand + + @return: string or None if it fails + """ + return idaapi.get_forced_operand(ea, n) + +ASCSTR_C = idaapi.ASCSTR_TERMCHR # C-style ASCII string +ASCSTR_PASCAL = idaapi.ASCSTR_PASCAL # Pascal-style ASCII string (length byte) +ASCSTR_LEN2 = idaapi.ASCSTR_LEN2 # Pascal-style, length is 2 bytes +ASCSTR_UNICODE = idaapi.ASCSTR_UNICODE # Unicode string +ASCSTR_LEN4 = idaapi.ASCSTR_LEN4 # Pascal-style, length is 4 bytes +ASCSTR_ULEN2 = idaapi.ASCSTR_ULEN2 # Pascal-style Unicode, length is 2 bytes +ASCSTR_ULEN4 = idaapi.ASCSTR_ULEN4 # Pascal-style Unicode, length is 4 bytes +ASCSTR_LAST = idaapi.ASCSTR_LAST # Last string type + +def GetString(ea, length = -1, strtype = ASCSTR_C): + """ + Get string contents + @param ea: linear address + @param length: string length. -1 means to calculate the max string length + @param strtype: the string type (one of ASCSTR_... constants) + + @return: string contents or empty string + """ + if length == -1: + length = idaapi.get_max_ascii_length(ea, strtype) + + return idaapi.get_ascii_contents(ea, length, strtype) + + +def GetStringType(ea): + """ + Get string type + + @param ea: linear address + + @return: One of ASCSTR_... constants + """ + ti = idaapi.opinfo_t() + + if idaapi.get_opinfo(ea, 0, GetFlags(ea), ti): + return ti.strtype + else: + return None + +# The following functions search for the specified byte +# ea - address to start from +# flag is combination of the following bits + +# returns BADADDR - not found +def FindVoid (ea, flag): return idaapi.find_void(ea, flag) +def FindCode (ea, flag): return idaapi.find_code(ea, flag) +def FindData (ea, flag): return idaapi.find_data(ea, flag) +def FindUnexplored (ea, flag): return idaapi.find_unknown(ea, flag) +def FindExplored (ea, flag): return idaapi.find_defined(ea, flag) +def FindImmediate (ea, flag, value): return idaapi.find_imm(ea, flag, value) + +SEARCH_UP = idaapi.SEARCH_UP # search backward +SEARCH_DOWN = idaapi.SEARCH_DOWN # search forward +SEARCH_NEXT = idaapi.SEARCH_NEXT # start the search at the next/prev item +SEARCH_CASE = idaapi.SEARCH_CASE # search case-sensitive + # (only for bin&txt search) +SEARCH_REGEX = idaapi.SEARCH_REGEX # enable regular expressions (only for text) +SEARCH_NOBRK = idaapi.SEARCH_NOBRK # don't test ctrl-break +SEARCH_NOSHOW = idaapi.SEARCH_NOSHOW # don't display the search progress + +def FindText(ea, flag, y, x, searchstr): + """ + @param ea: start address + @param flag: combination of SEARCH_* flags + @param y: number of text line at ea to start from (0..MAX_ITEM_LINES) + @param x: coordinate in this line + @param searchstr: search string + + @return: ea of result or BADADDR if not found + """ + return idaapi.find_text(ea, y, x, searchstr, flag) + + +def FindBinary(ea, flag, searchstr, radix=16): + """ + @param ea: start address + @param flag: combination of SEARCH_* flags + @param searchstr: a string as a user enters it for Search Text in Core + @param radix: radix of the numbers (default=16) + + @return: ea of result or BADADDR if not found + + @note: Example: "41 42" - find 2 bytes 41h,42h (radix is 16) + """ + endea = flag & 1 and idaapi.cvar.inf.maxEA or idaapi.cvar.inf.minEA + return idaapi.find_binary(ea, endea, searchstr, radix, flag) + + +#---------------------------------------------------------------------------- +# G L O B A L S E T T I N G S M A N I P U L A T I O N +#---------------------------------------------------------------------------- +def ChangeConfig(directive): + """ + Parse one or more ida.cfg config directives + @param directive: directives to process, for example: PACK_DATABASE=2 + + @note: If the directives are erroneous, a fatal error will be generated. + The changes will be effective only for the current session. + """ + return Eval('ChangeConfig("%s")' % directive) + + +# The following functions allow you to set/get common parameters. +# Please note that not all parameters can be set directly. + +def GetLongPrm(offset): + """ + """ + val = _IDC_GetAttr(idaapi.cvar.inf, _INFMAP, offset) + if offset == INF_PROCNAME: + # procName is a character array + # strip it at the terminating zero + idx = val.find('\0') + if idx != -1: + val = val[:idx] + return val + +def GetShortPrm(offset): + return GetLongPrm(offset) + + +def GetCharPrm (offset): + return GetLongPrm(offset) + + +def SetLongPrm (offset, value): + """ + """ + if offset == INF_PROCNAME: + raise NotImplementedError, "Please use idaapi.set_processor_type() to change processor" + return _IDC_SetAttr(idaapi.cvar.inf, _INFMAP, offset, value) + + +def SetShortPrm(offset, value): + SetLongPrm(offset, value) + + +def SetCharPrm (offset, value): + SetLongPrm(offset, value) + + +INF_VERSION = 3 # short; Version of database +INF_PROCNAME = 5 # char[8]; Name of current processor +INF_LFLAGS = 13 # char; IDP-dependent flags +LFLG_PC_FPP = 0x01 # decode floating point processor + # instructions? +LFLG_PC_FLAT = 0x02 # Flat model? +LFLG_64BIT = 0x04 # 64-bit program? +LFLG_DBG_NOPATH = 0x08 # do not store input full path +LFLG_SNAPSHOT = 0x10 # is memory snapshot? + # in debugger process options +INF_DEMNAMES = 14 # char; display demangled names as: +DEMNAM_CMNT = 0 # comments +DEMNAM_NAME = 1 # regular names +DEMNAM_NONE = 2 # don't display +INF_FILETYPE = 15 # short; type of input file (see ida.hpp) +FT_EXE_OLD = 0 # MS DOS EXE File (obsolete) +FT_COM_OLD = 1 # MS DOS COM File (obsolete) +FT_BIN = 2 # Binary File +FT_DRV = 3 # MS DOS Driver +FT_WIN = 4 # New Executable (NE) +FT_HEX = 5 # Intel Hex Object File +FT_MEX = 6 # MOS Technology Hex Object File +FT_LX = 7 # Linear Executable (LX) +FT_LE = 8 # Linear Executable (LE) +FT_NLM = 9 # Netware Loadable Module (NLM) +FT_COFF = 10 # Common Object File Format (COFF) +FT_PE = 11 # Portable Executable (PE) +FT_OMF = 12 # Object Module Format +FT_SREC = 13 # R-records +FT_ZIP = 14 # ZIP file (this file is never loaded to IDA database) +FT_OMFLIB = 15 # Library of OMF Modules +FT_AR = 16 # ar library +FT_LOADER = 17 # file is loaded using LOADER DLL +FT_ELF = 18 # Executable and Linkable Format (ELF) +FT_W32RUN = 19 # Watcom DOS32 Extender (W32RUN) +FT_AOUT = 20 # Linux a.out (AOUT) +FT_PRC = 21 # PalmPilot program file +FT_EXE = 22 # MS DOS EXE File +FT_COM = 23 # MS DOS COM File +FT_AIXAR = 24 # AIX ar library +INF_FCORESIZ = 17 +INF_CORESTART = 21 +INF_OSTYPE = 25 # short; FLIRT: OS type the program is for +OSTYPE_MSDOS = 0x0001 +OSTYPE_WIN = 0x0002 +OSTYPE_OS2 = 0x0004 +OSTYPE_NETW = 0x0008 +INF_APPTYPE = 27 # short; FLIRT: Application type +APPT_CONSOLE = 0x0001 # console +APPT_GRAPHIC = 0x0002 # graphics +APPT_PROGRAM = 0x0004 # EXE +APPT_LIBRARY = 0x0008 # DLL +APPT_DRIVER = 0x0010 # DRIVER +APPT_1THREAD = 0x0020 # Singlethread +APPT_MTHREAD = 0x0040 # Multithread +APPT_16BIT = 0x0080 # 16 bit application +APPT_32BIT = 0x0100 # 32 bit application +INF_START_SP = 29 # long; SP register value at the start of + # program execution +INF_START_AF = 33 # short; Analysis flags: +AF_FIXUP = 0x0001 # Create offsets and segments using fixup info +AF_MARKCODE = 0x0002 # Mark typical code sequences as code +AF_UNK = 0x0004 # Delete instructions with no xrefs +AF_CODE = 0x0008 # Trace execution flow +AF_PROC = 0x0010 # Create functions if call is present +AF_USED = 0x0020 # Analyze and create all xrefs +AF_FLIRT = 0x0040 # Use flirt signatures +AF_PROCPTR = 0x0080 # Create function if data xref data->code32 exists +AF_JFUNC = 0x0100 # Rename jump functions as j_... +AF_NULLSUB = 0x0200 # Rename empty functions as nullsub_... +AF_LVAR = 0x0400 # Create stack variables +AF_TRACE = 0x0800 # Trace stack pointer +AF_ASCII = 0x1000 # Create ascii string if data xref exists +AF_IMMOFF = 0x2000 # Convert 32bit instruction operand to offset +AF_DREFOFF = 0x4000 # Create offset if data xref to seg32 exists +AF_FINAL = 0x8000 # Final pass of analysis +INF_START_IP = 35 # long; IP register value at the start of + # program execution +INF_BEGIN_EA = 39 # long; Linear address of program entry point +INF_MIN_EA = 43 # long; The lowest address used + # in the program +INF_MAX_EA = 47 # long; The highest address used + # in the program - = 1 +INF_OMIN_EA = 51 +INF_OMAX_EA = 55 +INF_LOW_OFF = 59 # long; low limit of voids +INF_HIGH_OFF = 63 # long; high limit of voids +INF_MAXREF = 67 # long; max xref depth +INF_ASCII_BREAK = 71 # char; ASCII line break symbol +INF_WIDE_HIGH_BYTE_FIRST = 72 +INF_INDENT = 73 # char; Indention for instructions +INF_COMMENT = 74 # char; Indention for comments +INF_XREFNUM = 75 # char; Number of references to generate + # = 0 - xrefs wont be generated at all +INF_ENTAB = 76 # char; Use '\t' chars in the output file? +INF_SPECSEGS = 77 +INF_VOIDS = 78 # char; Display void marks? +INF_SHOWAUTO = 80 # char; Display autoanalysis indicator? +INF_AUTO = 81 # char; Autoanalysis is enabled? +INF_BORDER = 82 # char; Generate borders? +INF_NULL = 83 # char; Generate empty lines? +INF_GENFLAGS = 84 # char; General flags: +INFFL_LZERO = 0x01 # generate leading zeroes in numbers +INFFL_LOADIDC = 0x04 # Loading an idc file t +INF_SHOWPREF = 85 # char; Show line prefixes? +INF_PREFSEG = 86 # char; line prefixes with segment name? +INF_ASMTYPE = 87 # char; target assembler number (0..n) +INF_BASEADDR = 88 # long; base paragraph of the program +INF_XREFS = 92 # char; xrefs representation: +SW_SEGXRF = 0x01 # show segments in xrefs? +SW_XRFMRK = 0x02 # show xref type marks? +SW_XRFFNC = 0x04 # show function offsets? +SW_XRFVAL = 0x08 # show xref values? (otherwise-"...") +INF_BINPREF = 93 # short; # of instruction bytes to show + # in line prefix +INF_CMTFLAG = 95 # char; comments: +SW_RPTCMT = 0x01 # show repeatable comments? +SW_ALLCMT = 0x02 # comment all lines? +SW_NOCMT = 0x04 # no comments at all +SW_LINNUM = 0x08 # show source line numbers +SW_MICRO = 0x10 # show microcode (if implemented) +INF_NAMETYPE = 96 # char; dummy names represenation type +NM_REL_OFF = 0 +NM_PTR_OFF = 1 +NM_NAM_OFF = 2 +NM_REL_EA = 3 +NM_PTR_EA = 4 +NM_NAM_EA = 5 +NM_EA = 6 +NM_EA4 = 7 +NM_EA8 = 8 +NM_SHORT = 9 +NM_SERIAL = 10 +INF_SHOWBADS = 97 # char; show bad instructions? + # an instruction is bad if it appears + # in the ash.badworks array + +INF_PREFFLAG = 98 # char; line prefix type: +PREF_SEGADR = 0x01 # show segment addresses? +PREF_FNCOFF = 0x02 # show function offsets? +PREF_STACK = 0x04 # show stack pointer? + +INF_PACKBASE = 99 # char; pack database? + +INF_ASCIIFLAGS = 100 # uchar; ascii flags +ASCF_GEN = 0x01 # generate ASCII names? +ASCF_AUTO = 0x02 # ASCII names have 'autogenerated' bit? +ASCF_SERIAL = 0x04 # generate serial names? +ASCF_COMMENT = 0x10 # generate auto comment for ascii references? +ASCF_SAVECASE = 0x20 # preserve case of ascii strings for identifiers + +INF_LISTNAMES = 101 # uchar; What names should be included in the list? +LN_NORMAL = 0x01 # normal names +LN_PUBLIC = 0x02 # public names +LN_AUTO = 0x04 # autogenerated names +LN_WEAK = 0x08 # weak names + +INF_ASCIIPREF = 102 # char[16];ASCII names prefix +INF_ASCIISERNUM = 118 # ulong; serial number +INF_ASCIIZEROES = 122 # char; leading zeroes +INF_MF = 126 # uchar; Byte order: 1==MSB first +INF_ORG = 127 # char; Generate 'org' directives? +INF_ASSUME = 128 # char; Generate 'assume' directives? +INF_CHECKARG = 129 # char; Check manual operands? +INF_START_SS = 130 # long; value of SS at the start +INF_START_CS = 134 # long; value of CS at the start +INF_MAIN = 138 # long; address of main() +INF_SHORT_DN = 142 # long; short form of demangled names +INF_LONG_DN = 146 # long; long form of demangled names + # see demangle.h for definitions +INF_DATATYPES = 150 # long; data types allowed in data carousel +INF_STRTYPE = 154 # long; current ascii string type + # is considered as several bytes: + # low byte: +ASCSTR_TERMCHR = 0 # Character-terminated ASCII string +ASCSTR_C = 0 # C-string, zero terminated +ASCSTR_PASCAL = 1 # Pascal-style ASCII string (length byte) +ASCSTR_LEN2 = 2 # Pascal-style, length is 2 bytes +ASCSTR_UNICODE = 3 # Unicode string +ASCSTR_LEN4 = 4 # Delphi string, length is 4 bytes +ASCSTR_ULEN2 = 5 # Pascal-style Unicode, length is 2 bytes +ASCSTR_ULEN4 = 6 # Pascal-style Unicode, length is 4 bytes + +# = 2nd byte - termination chracters for ASCSTR_TERMCHR: +#STRTERM1(strtype) ((strtype>>8)&0xFF) +# = 3d byte: +#STRTERM2(strtype) ((strtype>>16)&0xFF) + # The termination characters are kept in + # the = 2nd and 3d bytes of string type + # if the second termination character is + # '\0', then it is ignored. +INF_AF2 = 158 # ushort; Analysis flags 2 +AF2_JUMPTBL = 0x0001 # Locate and create jump tables +AF2_DODATA = 0x0002 # Coagulate data segs in final pass +AF2_HFLIRT = 0x0004 # Automatically hide library functions +AF2_STKARG = 0x0008 # Propagate stack argument information +AF2_REGARG = 0x0010 # Propagate register argument information +AF2_CHKUNI = 0x0020 # Check for unicode strings +AF2_SIGCMT = 0x0040 # Append a signature name comment for recognized anonymous library functions +AF2_SIGMLT = 0x0080 # Allow recognition of several copies of the same function +AF2_FTAIL = 0x0100 # Create function tails +AF2_DATOFF = 0x0200 # Automatically convert data to offsets +AF2_ANORET = 0x0400 # Perform 'no-return' analysis +AF2_VERSP = 0x0800 # Perform full stack pointer analysis +AF2_DOCODE = 0x1000 # Coagulate code segs at the final pass +AF2_TRFUNC = 0x2000 # Truncate functions upon code deletion +AF2_PURDAT = 0x4000 # Control flow to data segment is ignored +INF_NAMELEN = 160 # ushort; max name length (without zero byte) +INF_MARGIN = 162 # ushort; max length of data lines +INF_LENXREF = 164 # ushort; max length of line with xrefs +INF_LPREFIX = 166 # char[16];prefix of local names + # if a new name has this prefix, + # it will be automatically converted to a local name +INF_LPREFIXLEN = 182 # uchar; length of the lprefix +INF_COMPILER = 183 # uchar; compiler +COMP_MASK = 0x0F # mask to apply to get the pure compiler id +COMP_UNK = 0x00 # Unknown +COMP_MS = 0x01 # Visual C++ +COMP_BC = 0x02 # Borland C++ +COMP_WATCOM = 0x03 # Watcom C++ +COMP_GNU = 0x06 # GNU C++ +COMP_VISAGE = 0x07 # Visual Age C++ +COMP_BP = 0x08 # Delphi + +INF_MODEL = 184 # uchar; memory model & calling convention +INF_SIZEOF_INT = 185 # uchar; sizeof(int) +INF_SIZEOF_BOOL = 186 # uchar; sizeof(bool) +INF_SIZEOF_ENUM = 187 # uchar; sizeof(enum) +INF_SIZEOF_ALGN = 188 # uchar; default alignment +INF_SIZEOF_SHORT = 189 +INF_SIZEOF_LONG = 190 +INF_SIZEOF_LLONG = 191 +INF_CHANGE_COUNTER = 192 # database change counter; keeps track of byte and segment modifications +INF_SIZEOF_LDBL = 196 # uchar; sizeof(long double) + +# Redefine these offsets for 64-bit version +if __EA64__: + INF_CORESTART = 25 + INF_OSTYPE = 33 + INF_APPTYPE = 35 + INF_START_SP = 37 + INF_AF = 45 + INF_START_IP = 47 + INF_BEGIN_EA = 55 + INF_MIN_EA = 63 + INF_MAX_EA = 71 + INF_OMIN_EA = 79 + INF_OMAX_EA = 87 + INF_LOW_OFF = 95 + INF_HIGH_OFF = 103 + INF_MAXREF = 111 + INF_ASCII_BREAK = 119 + INF_WIDE_HIGH_BYTE_FIRST = 120 + INF_INDENT = 121 + INF_COMMENT = 122 + INF_XREFNUM = 123 + INF_ENTAB = 124 + INF_SPECSEGS = 125 + INF_VOIDS = 126 + INF_SHOWAUTO = 128 + INF_AUTO = 129 + INF_BORDER = 130 + INF_NULL = 131 + INF_GENFLAGS = 132 + INF_SHOWPREF = 133 + INF_PREFSEG = 134 + INF_ASMTYPE = 135 + INF_BASEADDR = 136 + INF_XREFS = 144 + INF_BINPREF = 145 + INF_CMTFLAG = 147 + INF_NAMETYPE = 148 + INF_SHOWBADS = 149 + INF_PREFFLAG = 150 + INF_PACKBASE = 151 + INF_ASCIIFLAGS = 152 + INF_LISTNAMES = 153 + INF_ASCIIPREF = 154 + INF_ASCIISERNUM = 170 + INF_ASCIIZEROES = 178 + INF_MF = 182 + INF_ORG = 183 + INF_ASSUME = 184 + INF_CHECKARG = 185 + INF_START_SS = 186 + INF_START_CS = 194 + INF_MAIN = 202 + INF_SHORT_DN = 210 + INF_LONG_DN = 218 + INF_DATATYPES = 226 + INF_STRTYPE = 234 + INF_AF2 = 242 + INF_NAMELEN = 244 + INF_MARGIN = 246 + INF_LENXREF = 248 + INF_LPREFIX = 250 + INF_LPREFIXLEN = 266 + INF_COMPILER = 267 + INF_MODEL = 268 + INF_SIZEOF_INT = 269 + INF_SIZEOF_BOOL = 270 + INF_SIZEOF_ENUM = 271 + INF_SIZEOF_ALGN = 272 + INF_SIZEOF_SHORT = 273 + INF_SIZEOF_LONG = 274 + INF_SIZEOF_LLONG = 275 + INF_CHANGE_COUNTER = 276 + INF_SIZEOF_LBDL = 280 + +_INFMAP = { +INF_VERSION : (False, 'version'), # short; Version of database +INF_PROCNAME : (False, 'procName'), # char[8]; Name of current processor +INF_LFLAGS : (False, 'lflags'), # char; IDP-dependent flags +INF_DEMNAMES : (False, 'demnames'), # char; display demangled names as: +INF_FILETYPE : (False, 'filetype'), # short; type of input file (see ida.hpp) +INF_FCORESIZ : (False, 'fcoresize'), +INF_CORESTART : (False, 'corestart'), +INF_OSTYPE : (False, 'ostype'), # short; FLIRT: OS type the program is for +INF_APPTYPE : (False, 'apptype'), # short; FLIRT: Application type +INF_START_SP : (False, 'startSP'), # long; SP register value at the start of +INF_START_AF : (False, 'af'), # short; Analysis flags: +INF_START_IP : (False, 'startIP'), # long; IP register value at the start of +INF_BEGIN_EA : (False, 'beginEA'), # long; Linear address of program entry point +INF_MIN_EA : (False, 'minEA'), # long; The lowest address used +INF_MAX_EA : (False, 'maxEA'), # long; The highest address used +INF_OMIN_EA : (False, 'ominEA'), +INF_OMAX_EA : (False, 'omaxEA'), +INF_LOW_OFF : (False, 'lowoff'), # long; low limit of voids +INF_HIGH_OFF : (False, 'highoff'), # long; high limit of voids +INF_MAXREF : (False, 'maxref'), # long; max xref depth +INF_ASCII_BREAK : (False, 'ASCIIbreak'), # char; ASCII line break symbol +INF_WIDE_HIGH_BYTE_FIRST : (False, 'wide_high_byte_first'), +INF_INDENT : (False, 'indent'), # char; Indention for instructions +INF_COMMENT : (False, 'comment'), # char; Indention for comments +INF_XREFNUM : (False, 'xrefnum'), # char; Number of references to generate +INF_ENTAB : (False, 's_entab'), # char; Use '\t' chars in the output file? +INF_SPECSEGS : (False, 'specsegs'), +INF_VOIDS : (False, 's_void'), # char; Display void marks? +INF_SHOWAUTO : (False, 's_showauto'), # char; Display autoanalysis indicator? +INF_AUTO : (False, 's_auto'), # char; Autoanalysis is enabled? +INF_BORDER : (False, 's_limiter'), # char; Generate borders? +INF_NULL : (False, 's_null'), # char; Generate empty lines? +INF_GENFLAGS : (False, 's_genflags'), # char; General flags: +INF_SHOWPREF : (False, 's_showpref'), # char; Show line prefixes? +INF_PREFSEG : (False, 's_prefseg'), # char; line prefixes with segment name? +INF_ASMTYPE : (False, 'asmtype'), # char; target assembler number (0..n) +INF_BASEADDR : (False, 'baseaddr'), # long; base paragraph of the program +INF_XREFS : (False, 's_xrefflag'), # char; xrefs representation: +INF_BINPREF : (False, 'binSize'), # short; # of instruction bytes to show +INF_CMTFLAG : (False, 's_cmtflg'), # char; comments: +INF_NAMETYPE : (False, 'nametype'), # char; dummy names represenation type +INF_SHOWBADS : (False, 's_showbads'), # char; show bad instructions? +INF_PREFFLAG : (False, 's_prefflag'), # char; line prefix type: +INF_PACKBASE : (False, 's_packbase'), # char; pack database? +INF_ASCIIFLAGS : (False, 'asciiflags'), # uchar; ascii flags +INF_LISTNAMES : (False, 'listnames'), # uchar; What names should be included in the list? +INF_ASCIIPREF : (False, 'ASCIIpref'), # char[16];ASCII names prefix +INF_ASCIISERNUM : (False, 'ASCIIsernum'), # ulong; serial number +INF_ASCIIZEROES : (False, 'ASCIIzeroes'), # char; leading zeroes +INF_MF : (False, 'mf'), # uchar; Byte order: 1==MSB first +INF_ORG : (False, 's_org'), # char; Generate 'org' directives? +INF_ASSUME : (False, 's_assume'), # char; Generate 'assume' directives? +INF_CHECKARG : (False, 's_checkarg'), # char; Check manual operands? +INF_START_SS : (False, 'start_ss'), # long; value of SS at the start +INF_START_CS : (False, 'start_cs'), # long; value of CS at the start +INF_MAIN : (False, 'main'), # long; address of main() +INF_SHORT_DN : (False, 'short_demnames'), # long; short form of demangled names +INF_LONG_DN : (False, 'long_demnames'), # long; long form of demangled names +INF_DATATYPES : (False, 'datatypes'), # long; data types allowed in data carousel +INF_STRTYPE : (False, 'strtype'), # long; current ascii string type +INF_AF2 : (False, 'af2'), # ushort; Analysis flags 2 +INF_NAMELEN : (False, 'namelen'), # ushort; max name length (without zero byte) +INF_MARGIN : (False, 'margin'), # ushort; max length of data lines +INF_LENXREF : (False, 'lenxref'), # ushort; max length of line with xrefs +INF_LPREFIX : (False, 'lprefix'), # char[16];prefix of local names +INF_LPREFIXLEN : (False, 'lprefixlen'), # uchar; length of the lprefix +INF_COMPILER : (False, 'cc') # uchar; compiler + +#INF_MODEL = 184 # uchar; memory model & calling convention +#INF_SIZEOF_INT = 185 # uchar; sizeof(int) +#INF_SIZEOF_BOOL = 186 # uchar; sizeof(bool) +#INF_SIZEOF_ENUM = 187 # uchar; sizeof(enum) +#INF_SIZEOF_ALGN = 188 # uchar; default alignment +#INF_SIZEOF_SHORT = 189 +#INF_SIZEOF_LONG = 190 +#INF_SIZEOF_LLONG = 191 +} + + +def SetProcessorType (processor, level): + """ + Change current processor + + @param processor: name of processor in short form. + run 'ida ?' to get list of allowed processor types + @param level: the power of request: + - SETPROC_COMPAT - search for the processor type in the current module + - SETPROC_ALL - search for the processor type in all modules + only if there were not calls with SETPROC_USER + - SETPROC_USER - search for the processor type in all modules + and prohibit level SETPROC_USER + - SETPROC_FATAL - can be combined with previous bits. + means that if the processor type can't be + set, IDA should display an error message and exit. + """ + return idaapi.set_processor_type(processor, level) + +SETPROC_COMPAT = idaapi.SETPROC_COMPAT +SETPROC_ALL = idaapi.SETPROC_ALL +SETPROC_USER = idaapi.SETPROC_USER +SETPROC_FATAL = idaapi.SETPROC_FATAL + +def SetPrcsr(processor): return SetProcessorType(processor, SETPROC_COMPAT) + + +def Batch(batch): + """ + Enable/disable batch mode of operation + + @param batch: Batch mode + 0 - ida will display dialog boxes and wait for the user input + 1 - ida will not display dialog boxes, warnings, etc. + + @return: old balue of batch flag + """ + batch_prev = idaapi.cvar.batch + idaapi.cvar.batch = batch + return batch_prev + + +#---------------------------------------------------------------------------- +# I N T E R A C T I O N W I T H T H E U S E R +#---------------------------------------------------------------------------- +def AskStr(defval, prompt): + """ + Ask the user to enter a string + + @param defval: the default string value. This value will appear + in the dialog box. + @param prompt: the prompt to display in the dialog box + + @return: the entered string or 0. + """ + return idaapi.askstr(0, defval, prompt) + + +def AskFile(forsave, mask, prompt): + """ + Ask the user to choose a file + + @param forsave: 0: "Open" dialog box, 1: "Save" dialog box + @param mask: the input file mask as "*.*" or the default file name. + @param prompt: the prompt to display in the dialog box + + @return: the selected file or 0. + """ + return idaapi.askfile_c(forsave, mask, prompt) + + +def AskAddr(defval, prompt): + """ + Ask the user to enter an address + + @param defval: the default address value. This value + will appear in the dialog box. + @param prompt: the prompt to display in the dialog box + + @return: the entered address or BADADDR. + """ + return idaapi.askaddr(defval, prompt) + + +def AskLong(defval, prompt): + """ + Ask the user to enter a number + + @param defval: the default value. This value + will appear in the dialog box. + @param prompt: the prompt to display in the dialog box + + @return: the entered number or -1. + """ + return idaapi.asklong(defval, prompt) + + +def AskSeg(defval, prompt): + """ + Ask the user to enter a segment value + + @param defval: the default value. This value + will appear in the dialog box. + @param prompt: the prompt to display in the dialog box + + @return: the entered segment selector or BADSEL. + """ + return idaapi.askseg(defval, prompt) + + +def AskIdent(defval, prompt): + """ + Ask the user to enter an identifier + + @param defval: the default identifier. This value will appear in + the dialog box. + @param prompt: the prompt to display in the dialog box + + @return: the entered identifier or 0. + """ + return idaapi.askident(defval, prompt) + + +def AskYN(defval, prompt): + """ + Ask the user a question and let him answer Yes/No/Cancel + + @param defval: the default answer. This answer will be selected if the user + presses Enter. -1:cancel,0-no,1-ok + @param prompt: the prompt to display in the dialog box + + @return: -1:cancel,0-no,1-ok + """ + return idaapi.askyn_c(defval, prompt) + + +def Message(msg): + """ + Display a message in the message window + + @param msg: message to print (formatting is done in Python) + + This function can be used to debug IDC scripts + """ + idaapi.msg(msg) + + +def Warning(msg): + """ + Display a message in a message box + + @param msg: message to print (formatting is done in Python) + + This function can be used to debug IDC scripts + The user will be able to hide messages if they appear twice in a row on + the screen + """ + idaapi.warning(msg) + + +def Fatal(format): + """ + Display a fatal message in a message box and quit IDA + + @param format: message to print + """ + idaapi.error(format) + + +def SetStatus(status): + """ + Change IDA indicator. + + @param status: new status + + @return: the previous status. + """ + return idaapi.setStat(status) + + +IDA_STATUS_READY = 0 # READY IDA is idle +IDA_STATUS_THINKING = 1 # THINKING Analyzing but the user may press keys +IDA_STATUS_WAITING = 2 # WAITING Waiting for the user input +IDA_STATUS_WORK = 3 # BUSY IDA is busy + + +def Refresh(): + """ + Refresh all disassembly views + """ + idaapi.refresh_idaview_anyway() + + +def RefreshLists(): + """ + Refresh all list views (names, functions, etc) + """ + idaapi.refresh_lists() + + +#---------------------------------------------------------------------------- +# S E G M E N T A T I O N +#---------------------------------------------------------------------------- +def AskSelector(sel): + """ + Get a selector value + + @param sel: the selector number + + @return: selector value if found + otherwise the input value (sel) + + @note: selector values are always in paragraphs + """ + s = idaapi.sel_pointer() + base = idaapi.ea_pointer() + res,tmp = idaapi.getn_selector(sel, s.cast(), base.cast()) + + if not res: + return sel + else: + return base.value() + + +def FindSelector(val): + """ + Find a selector which has the specifed value + + @param val: value to search for + + @return: the selector number if found, + otherwise the input value (val & 0xFFFF) + + @note: selector values are always in paragraphs + """ + return idaapi.find_selector(val) & 0xFFFF + + +def SetSelector(sel, value): + """ + Set a selector value + + @param sel: the selector number + @param value: value of selector + + @return: None + + @note: ida supports up to 4096 selectors. + if 'sel' == 'val' then the selector is destroyed because + it has no significance + """ + return idaapi.set_selector(sel, value) + + +def DelSelector(sel): + """ + Delete a selector + + @param sel: the selector number to delete + + @return: None + + @note: if the selector is found, it will be deleted + """ + return idaapi.del_selector(sel) + + +def FirstSeg(): + """ + Get first segment + + @return: address of the start of the first segment + BADADDR - no segments are defined + """ + seg = idaapi.get_first_seg() + if not seg: + return BADADDR + else: + return seg.startEA + + +def NextSeg(ea): + """ + Get next segment + + @param ea: linear address + + @return: start of the next segment + BADADDR - no next segment + """ + nextseg = idaapi.get_next_seg(ea) + if not nextseg: + return BADADDR + else: + return nextseg.startEA + + return BADADDR + + +def SegStart(ea): + """ + Get start address of a segment + + @param ea: any address in the segment + + @return: start of segment + BADADDR - the specified address doesn't belong to any segment + """ + seg = idaapi.getseg(ea) + + if not seg: + return BADADDR + else: + return seg.startEA + + +def SegEnd(ea): + """ + Get end address of a segment + + @param ea: any address in the segment + + @return: end of segment (an address past end of the segment) + BADADDR - the specified address doesn't belong to any segment + """ + seg = idaapi.getseg(ea) + + if not seg: + return BADADDR + else: + return seg.endEA + + +def SegName(ea): + """ + Get name of a segment + + @param ea: any address in the segment + + @return: "" - no segment at the specified address + """ + seg = idaapi.getseg(ea) + + if not seg: + return "" + else: + name = idaapi.get_true_segm_name(seg) + + if not name: + return "" + else: + return name + + +def AddSeg(startea, endea, base, use32, align, comb): + """ + Create a new segment + + @param startea: linear address of the start of the segment + @param endea: linear address of the end of the segment + this address will not belong to the segment + 'endea' should be higher than 'startea' + @param base: base paragraph or selector of the segment. + a paragraph is 16byte memory chunk. + If a selector value is specified, the selector should be + already defined. + @param use32: 0: 16bit segment, 1: 32bit segment, 2: 64bit segment + @param align: segment alignment. see below for alignment values + @param comb: segment combination. see below for combination values. + + @return: 0-failed, 1-ok + """ + s = idaapi.segment_t() + s.startEA = startea + s.endEA = endea + s.sel = idaapi.setup_selector(base) + s.bitness = use32 + s.align = align + s.comb = comb + return idaapi.add_segm_ex(s, "", "", idaapi.ADDSEG_NOSREG) + + +def DelSeg(ea, flags): + """ + Delete a segment + + @param ea: any address in the segment + @param flags: combination of SEGMOD_* flags + + @return: boolean success + """ + return idaapi.del_segm(ea, flags) + +SEGMOD_KILL = idaapi.SEGMOD_KILL # disable addresses if segment gets + # shrinked or deleted +SEGMOD_KEEP = idaapi.SEGMOD_KEEP # keep information (code & data, etc) +SEGMOD_SILENT = idaapi.SEGMOD_SILENT # be silent + + +def SetSegBounds(ea, startea, endea, flags): + """ + Change segment boundaries + + @param ea: any address in the segment + @param startea: new start address of the segment + @param endea: new end address of the segment + @param flags: combination of SEGMOD_... flags + + @return: boolean success + """ + return idaapi.set_segm_start(ea, startea, flags) & \ + idaapi.set_segm_end(ea, endea, flags) + + +def RenameSeg(ea, name): + """ + Change name of the segment + + @param ea: any address in the segment + @param name: new name of the segment + + @return: success (boolean) + """ + seg = idaapi.getseg(ea) + + if not seg: + return False + + return idaapi.set_segm_name(seg, name) + + +def SetSegClass(ea, segclass): + """ + Change class of the segment + + @param ea: any address in the segment + @param segclass: new class of the segment + + @return: success (boolean) + """ + seg = idaapi.getseg(ea) + + if not seg: + return False + + return idaapi.set_segm_class(seg, segclass) + + +def SegAlign(ea, alignment): + """ + Change alignment of the segment + + @param ea: any address in the segment + @param alignment: new alignment of the segment (one of the sa... constants) + + @return: success (boolean) + """ + return SetSegmentAttr(ea, SEGATTR_ALIGN, alignment) + + +saAbs = idaapi.saAbs # Absolute segment. +saRelByte = idaapi.saRelByte # Relocatable, byte aligned. +saRelWord = idaapi.saRelWord # Relocatable, word (2-byte, 16-bit) aligned. +saRelPara = idaapi.saRelPara # Relocatable, paragraph (16-byte) aligned. +saRelPage = idaapi.saRelPage # Relocatable, aligned on 256-byte boundary + # (a "page" in the original Intel specification). +saRelDble = idaapi.saRelDble # Relocatable, aligned on a double word + # (4-byte) boundary. This value is used by + # the PharLap OMF for the same alignment. +saRel4K = idaapi.saRel4K # This value is used by the PharLap OMF for + # page (4K) alignment. It is not supported + # by LINK. +saGroup = idaapi.saGroup # Segment group +saRel32Bytes = idaapi.saRel32Bytes # 32 bytes +saRel64Bytes = idaapi.saRel64Bytes # 64 bytes +saRelQword = idaapi.saRelQword # 8 bytes + + +def SegComb(segea, comb): + """ + Change combination of the segment + + @param segea: any address in the segment + @param comb: new combination of the segment (one of the sc... constants) + + @return: success (boolean) + """ + return SetSegmentAttr(segea, SEGATTR_COMB, comb) + + +scPriv = idaapi.scPriv # Private. Do not combine with any other program + # segment. +scPub = idaapi.scPub # Public. Combine by appending at an offset that + # meets the alignment requirement. +scPub2 = idaapi.scPub2 # As defined by Microsoft, same as C=2 (public). +scStack = idaapi.scStack # Stack. Combine as for C=2. This combine type + # forces byte alignment. +scCommon = idaapi.scCommon # Common. Combine by overlay using maximum size. +scPub3 = idaapi.scPub3 # As defined by Microsoft, same as C=2 (public). + + +def SetSegAddressing(ea, bitness): + """ + Change segment addressing + + @param ea: any address in the segment + @param bitness: 0: 16bit, 1: 32bit, 2: 64bit + + @return: success (boolean) + """ + seg = idaapi.getseg(ea) + + if not seg: + return False + + seg.bitness = bitness + + return True + + +def SegByName(segname): + """ + Get segment by name + + @param segname: name of segment + + @return: segment selector or BADADDR + """ + seg = idaapi.get_segm_by_name(segname) + + if not seg: + return BADADDR + + return seg.startEA + + +def SetSegDefReg(ea, reg, value): + """ + Set default segment register value for a segment + + @param ea: any address in the segment + if no segment is present at the specified address + then all segments will be affected + @param reg: name of segment register + @param value: default value of the segment register. -1-undefined. + """ + seg = idaapi.getseg(ea) + + reg = idaapi.str2reg(reg); + if seg and reg >= 0: + return idaapi.SetDefaultRegisterValue(seg, reg, value) + else: + return False + + +def SetSegmentType(segea, segtype): + """ + Set segment type + + @param segea: any address within segment + @param segtype: new segment type: + + @return: !=0 - ok + """ + seg = idaapi.getseg(segea) + + if not seg: + return False + + seg.type = segtype + return seg.update() + + +SEG_NORM = idaapi.SEG_NORM +SEG_XTRN = idaapi.SEG_XTRN # * segment with 'extern' definitions + # no instructions are allowed +SEG_CODE = idaapi.SEG_CODE # pure code segment +SEG_DATA = idaapi.SEG_DATA # pure data segment +SEG_IMP = idaapi.SEG_IMP # implementation segment +SEG_GRP = idaapi.SEG_GRP # * group of segments + # no instructions are allowed +SEG_NULL = idaapi.SEG_NULL # zero-length segment +SEG_UNDF = idaapi.SEG_UNDF # undefined segment type +SEG_BSS = idaapi.SEG_BSS # uninitialized segment +SEG_ABSSYM = idaapi.SEG_ABSSYM # * segment with definitions of absolute symbols + # no instructions are allowed +SEG_COMM = idaapi.SEG_COMM # * segment with communal definitions + # no instructions are allowed +SEG_IMEM = idaapi.SEG_IMEM # internal processor memory & sfr (8051) + + +def GetSegmentAttr(segea, attr): + """ + Get segment attribute + + @param segea: any address within segment + @param attr: one of SEGATTR_... constants + """ + seg = idaapi.getseg(segea) + assert seg, "could not find segment at 0x%x" % segea + if attr in [ SEGATTR_ES, SEGATTR_CS, SEGATTR_SS, SEGATTR_DS, SEGATTR_FS, SEGATTR_GS ]: + return idaapi.get_defsr(seg, _SEGATTRMAP[attr]) + else: + return _IDC_GetAttr(seg, _SEGATTRMAP, attr) + + +def SetSegmentAttr(segea, attr, value): + """ + Set segment attribute + + @param segea: any address within segment + @param attr: one of SEGATTR_... constants + + @note: Please note that not all segment attributes are modifiable. + Also some of them should be modified using special functions + like SetSegAddressing, etc. + """ + seg = idaapi.getseg(segea) + assert seg, "could not find segment at 0x%x" % segea + if attr in [ SEGATTR_ES, SEGATTR_CS, SEGATTR_SS, SEGATTR_DS, SEGATTR_FS, SEGATTR_GS ]: + idaapi.set_defsr(seg, _SEGATTRMAP[attr], value) + else: + _IDC_SetAttr(seg, _SEGATTRMAP, attr, value) + return seg.update() + + +SEGATTR_START = 0 # starting address +SEGATTR_END = 4 # ending address +SEGATTR_ORGBASE = 16 +SEGATTR_ALIGN = 20 # alignment +SEGATTR_COMB = 21 # combination +SEGATTR_PERM = 22 # permissions +SEGATTR_BITNESS = 23 # bitness (0: 16, 1: 32, 2: 64 bit segment) + # Note: modifying the attribute directly does + # not lead to the reanalysis of the segment. + # Using SetSegAddressing() is more correct. +SEGATTR_FLAGS = 24 # segment flags +SEGATTR_SEL = 26 # segment selector +SEGATTR_ES = 30 # default ES value +SEGATTR_CS = 34 # default CS value +SEGATTR_SS = 38 # default SS value +SEGATTR_DS = 42 # default DS value +SEGATTR_FS = 46 # default FS value +SEGATTR_GS = 50 # default GS value +SEGATTR_TYPE = 94 # segment type +SEGATTR_COLOR = 95 # segment color + +# Redefining these for 64-bit +if __EA64__: + SEGATTR_START = 0 + SEGATTR_END = 8 + SEGATTR_ORGBASE = 32 + SEGATTR_ALIGN = 40 + SEGATTR_COMB = 41 + SEGATTR_PERM = 42 + SEGATTR_BITNESS = 43 + SEGATTR_FLAGS = 44 + SEGATTR_SEL = 46 + SEGATTR_ES = 54 + SEGATTR_CS = 62 + SEGATTR_SS = 70 + SEGATTR_DS = 78 + SEGATTR_FS = 86 + SEGATTR_GS = 94 + SEGATTR_TYPE = 182 + SEGATTR_COLOR = 183 + +_SEGATTRMAP = { + SEGATTR_START : (True, 'startEA'), + SEGATTR_END : (True, 'endEA'), + SEGATTR_ORGBASE : (False, 'orgbase'), + SEGATTR_ALIGN : (False, 'align'), + SEGATTR_COMB : (False, 'comb'), + SEGATTR_PERM : (False, 'perm'), + SEGATTR_BITNESS : (False, 'bitness'), + SEGATTR_FLAGS : (False, 'flags'), + SEGATTR_SEL : (False, 'sel'), + SEGATTR_ES : (False, 0), + SEGATTR_CS : (False, 1), + SEGATTR_SS : (False, 2), + SEGATTR_DS : (False, 3), + SEGATTR_FS : (False, 4), + SEGATTR_GS : (False, 5), + SEGATTR_TYPE : (False, 'type'), + SEGATTR_COLOR : (False, 'color'), +} + +# Valid segment flags +SFL_COMORG = 0x01 # IDP dependent field (IBM PC: if set, ORG directive is not commented out) +SFL_OBOK = 0x02 # orgbase is present? (IDP dependent field) +SFL_HIDDEN = 0x04 # is the segment hidden? +SFL_DEBUG = 0x08 # is the segment created for the debugger? +SFL_LOADER = 0x10 # is the segment created by the loader? +SFL_HIDETYPE = 0x20 # hide segment type (do not print it in the listing) + + +def MoveSegm(ea, to, flags): + """ + Move a segment to a new address + This function moves all information to the new address + It fixes up address sensitive information in the kernel + The total effect is equal to reloading the segment to the target address + + @param ea: any address within the segment to move + @param to: new segment start address + @param flags: combination MFS_... constants + + @returns: MOVE_SEGM_... error code + """ + seg = idaapi.getseg(ea) + if not seg: + return MOVE_SEGM_PARAM + return idaapi.move_segm(seg, to, flags) + + +MSF_SILENT = 0x0001 # don't display a "please wait" box on the screen +MSF_NOFIX = 0x0002 # don't call the loader to fix relocations +MSF_LDKEEP = 0x0004 # keep the loader in the memory (optimization) +MSF_FIXONCE = 0x0008 # valid for rebase_program(): call loader only once + +MOVE_SEGM_OK = 0 # all ok +MOVE_SEGM_PARAM = -1 # The specified segment does not exist +MOVE_SEGM_ROOM = -2 # Not enough free room at the target address +MOVE_SEGM_IDP = -3 # IDP module forbids moving the segment +MOVE_SEGM_CHUNK = -4 # Too many chunks are defined, can't move +MOVE_SEGM_LOADER = -5 # The segment has been moved but the loader complained +MOVE_SEGM_ODD = -6 # Can't move segments by an odd number of bytes + + +def rebase_program(delta, flags): + """ + Rebase the whole program by 'delta' bytes + + @param delta: number of bytes to move the program + @param flags: combination of MFS_... constants + it is recommended to use MSF_FIXONCE so that the loader takes + care of global variables it stored in the database + + @returns: error code MOVE_SEGM_... + """ + return idaapi.rebase_program(delta, flags) + + +def SetStorageType(startEA, endEA, stt): + """ + Set storage type + + @param startEA: starting address + @param endEA: ending address + @param stt: new storage type, one of STT_VA and STT_MM + + @returns: 0 - ok, otherwise internal error code + """ + return idaapi.change_storage_type(startEA, endEA, stt) + + +STT_VA = 0 # regular storage: virtual arrays, an explicit flag for each byte +STT_MM = 1 # memory map: sparse storage. useful for huge objects + + +#---------------------------------------------------------------------------- +# C R O S S R E F E R E N C E S +#---------------------------------------------------------------------------- +# Flow types (combine with XREF_USER!): +fl_CF = 16 # Call Far +fl_CN = 17 # Call Near +fl_JF = 18 # Jump Far +fl_JN = 19 # Jump Near +fl_F = 21 # Ordinary flow + +XREF_USER = 32 # All user-specified xref types + # must be combined with this bit + + +# Mark exec flow 'from' 'to' +def AddCodeXref(From, To, flowtype): + """ + """ + return idaapi.add_cref(From, To, flowtype) + + +def DelCodeXref(From, To, undef): + """ + Unmark exec flow 'from' 'to' + + @param undef: make 'To' undefined if no more references to it + + @returns: 1 - planned to be made undefined + """ + return idaapi.del_cref(From, To, undef) + + +# The following functions include the ordinary flows: +# (the ordinary flow references are returned first) +def Rfirst(From): + """ + Get first code xref from 'From' + """ + return idaapi.get_first_cref_from(From) + + +def Rnext(From, current): + """ + Get next code xref from + """ + return idaapi.get_next_cref_from(From, current) + + +def RfirstB(To): + """ + Get first code xref to 'To' + """ + return idaapi.get_first_cref_to(To) + + +def RnextB(To, current): + """ + Get next code xref to 'To' + """ + return idaapi.get_next_cref_to(To, current) + + +# The following functions don't take into account the ordinary flows: +def Rfirst0(From): + """ + Get first xref from 'From' + """ + return idaapi.get_first_fcref_from(From) + + +def Rnext0(From, current): + """ + Get next xref from + """ + return idaapi.get_next_fcref_from(From, current) + + +def RfirstB0(To): + """ + Get first xref to 'To' + """ + return idaapi.get_first_fcref_to(To) + + +def RnextB0(To, current): + """ + Get next xref to 'To' + """ + return idaapi.get_next_fcref_to(To, current) + + +# Data reference types (combine with XREF_USER!): +dr_O = idaapi.dr_O # Offset +dr_W = idaapi.dr_W # Write +dr_R = idaapi.dr_R # Read +dr_T = idaapi.dr_T # Text (names in manual operands) +dr_I = idaapi.dr_I # Informational + + +def add_dref(From, To, drefType): + """ + Create Data Ref + """ + return idaapi.add_dref(From, To, drefType) + + +def del_dref(From, To): + """ + Unmark Data Ref + """ + return idaapi.del_dref(From, To) + + +def Dfirst(From): + """ + Get first data xref from 'From' + """ + return idaapi.get_first_dref_from(From) + + +def Dnext(From, current): + """ + Get next data xref from 'From' + """ + return idaapi.get_next_dref_from(From, current) + + +def DfirstB(To): + """ + Get first data xref to 'To' + """ + return idaapi.get_first_dref_to(To) + + +def DnextB(To, current): + """ + Get next data xref to 'To' + """ + return idaapi.get_next_dref_to(To, current) + + +def XrefType(): + """ + Return type of the last xref obtained by + [RD]first/next[B0] functions. + + @return: constants fl_* or dr_* + """ + raise DeprecatedIDCError, "use XrefsFrom() XrefsTo() from idautils instead." + + +#---------------------------------------------------------------------------- +# F I L E I / O +#---------------------------------------------------------------------------- +def fopen(f, mode): + raise DeprecatedIDCError, "fopen() deprecated. Use Python file objects instead." + +def fclose(handle): + raise DeprecatedIDCError, "fclose() deprecated. Use Python file objects instead." + +def filelength(handle): + raise DeprecatedIDCError, "filelength() deprecated. Use Python file objects instead." + +def fseek(handle, offset, origin): + raise DeprecatedIDCError, "fseek() deprecated. Use Python file objects instead." + +def ftell(handle): + raise DeprecatedIDCError, "ftell() deprecated. Use Python file objects instead." + + +def LoadFile(filepath, pos, ea, size): + """ + Load file into IDA database + + @param filepath: path to input file + @param pos: position in the file + @param ea: linear address to load + @param size: number of bytes to load + + @return: 0 - error, 1 - ok + """ + li = idaapi.open_linput(filepath, False) + + if li: + retval = idaapi.file2base(li, pos, ea, ea+size, False) + idaapi.close_linput(li) + return retval + else: + return 0 + +def loadfile(filepath, pos, ea, size): return LoadFile(filepath, pos, ea, size) + + +def SaveFile(filepath, pos, ea, size): + """ + Save from IDA database to file + + @param filepath: path to output file + @param pos: position in the file + @param ea: linear address to save from + @param size: number of bytes to save + + @return: 0 - error, 1 - ok + """ + of = idaapi.fopenWB(filepath) + + if of: + retval = idaapi.base2file(of, pos, ea, ea+size) + idaapi.eclose(of) + return retval + else: + return 0 + +def savefile(filepath, pos, ea, size): return SaveFile(filepath, pos, ea, size) + + +def fgetc(handle): + raise DeprecatedIDCError, "fgetc() deprecated. Use Python file objects instead." + +def fputc(byte, handle): + raise DeprecatedIDCError, "fputc() deprecated. Use Python file objects instead." + +def fprintf(handle, format, *args): + raise DeprecatedIDCError, "fprintf() deprecated. Use Python file objects instead." + +def readshort(handle, mostfirst): + raise DeprecatedIDCError, "readshort() deprecated. Use Python file objects instead." + +def readlong(handle, mostfirst): + raise DeprecatedIDCError, "readlong() deprecated. Use Python file objects instead." + +def writeshort(handle, word, mostfirst): + raise DeprecatedIDCError, "writeshort() deprecated. Use Python file objects instead." + +def writelong(handle, dword, mostfirst): + raise DeprecatedIDCError, "writelong() deprecated. Use Python file objects instead." + +def readstr(handle): + raise DeprecatedIDCError, "readstr() deprecated. Use Python file objects instead." + +def writestr(handle, s): + raise DeprecatedIDCError, "writestr() deprecated. Use Python file objects instead." + +# ---------------------------------------------------------------------------- +# F U N C T I O N S +# ---------------------------------------------------------------------------- + +def MakeFunction(start, end): + """ + Create a function + + @param start: function bounds + @param end: function bounds + + If the function end address is BADADDR, then + IDA will try to determine the function bounds + automatically. IDA will define all necessary + instructions to determine the function bounds. + + @return: !=0 - ok + + @note: an instruction should be present at the start address + """ + return idaapi.add_func(start, end) + + +def DelFunction(ea): + """ + Delete a function + + @param ea: any address belonging to the function + + @return: !=0 - ok + """ + return idaapi.del_func(ea) + + +def SetFunctionEnd(ea, end): + """ + Change function end address + + @param ea: any address belonging to the function + @param end: new function end address + + @return: !=0 - ok + """ + return idaapi.func_setend(ea, end) + + +def NextFunction(ea): + """ + Find next function + + @param ea: any address belonging to the function + + @return: -1 - no more functions + otherwise returns the next function start address + """ + func = idaapi.get_next_func(ea) + + if not func: + return BADADDR + else: + return func.startEA + + +def PrevFunction(ea): + """ + Find previous function + + @param ea: any address belonging to the function + + @return: -1 - no more functions + otherwise returns the previous function start address + """ + func = idaapi.get_prev_func(ea) + + if not func: + return BADADDR + else: + return func.startEA + + +def GetFunctionAttr(ea, attr): + """ + Get a function attribute + + @param ea: any address belonging to the function + @param attr: one of FUNCATTR_... constants + + @return: -1 - error otherwise returns the attribute value + """ + func = idaapi.get_func(ea) + + return _IDC_GetAttr(func, _FUNCATTRMAP, attr) if func else BADADDR + + +def SetFunctionAttr(ea, attr, value): + """ + Set a function attribute + + @param ea: any address belonging to the function + @param attr: one of FUNCATTR_... constants + @param value: new value of the attribute + + @return: 1-ok, 0-failed + """ + func = idaapi.get_func(ea) + + if func: + _IDC_SetAttr(func, _FUNCATTRMAP, attr, value) + return idaapi.update_func(func) + return 0 + + +FUNCATTR_START = 0 # function start address +FUNCATTR_END = 4 # function end address +FUNCATTR_FLAGS = 8 # function flags +FUNCATTR_FRAME = 10 # function frame id +FUNCATTR_FRSIZE = 14 # size of local variables +FUNCATTR_FRREGS = 18 # size of saved registers area +FUNCATTR_ARGSIZE = 20 # number of bytes purged from the stack +FUNCATTR_FPD = 24 # frame pointer delta +FUNCATTR_COLOR = 28 # function color code +FUNCATTR_OWNER = 10 # chunk owner (valid only for tail chunks) +FUNCATTR_REFQTY = 14 # number of chunk parents (valid only for tail chunks) + +# Redefining the constants for 64-bit +if __EA64__: + FUNCATTR_START = 0 + FUNCATTR_END = 8 + FUNCATTR_FLAGS = 16 + FUNCATTR_FRAME = 18 + FUNCATTR_FRSIZE = 26 + FUNCATTR_FRREGS = 34 + FUNCATTR_ARGSIZE = 36 + FUNCATTR_FPD = 44 + FUNCATTR_COLOR = 52 + FUNCATTR_OWNER = 18 + FUNCATTR_REFQTY = 26 + + +_FUNCATTRMAP = { + FUNCATTR_START : (True, 'startEA'), + FUNCATTR_END : (True, 'endEA'), + FUNCATTR_FLAGS : (False, 'flags'), + FUNCATTR_FRAME : (True, 'frame'), + FUNCATTR_FRSIZE : (True, 'frsize'), + FUNCATTR_FRREGS : (True, 'frregs'), + FUNCATTR_ARGSIZE : (True, 'argsize'), + FUNCATTR_FPD : (False, 'fpd'), + FUNCATTR_COLOR : (False, 'color'), + FUNCATTR_OWNER : (True, 'owner'), + FUNCATTR_REFQTY : (True, 'refqty') +} + + +def GetFunctionFlags(ea): + """ + Retrieve function flags + + @param ea: any address belonging to the function + + @return: -1 - function doesn't exist otherwise returns the flags + """ + func = idaapi.get_func(ea) + + if not func: + return -1 + else: + return func.flags + + +FUNC_NORET = idaapi.FUNC_NORET # function doesn't return +FUNC_FAR = idaapi.FUNC_FAR # far function +FUNC_LIB = idaapi.FUNC_LIB # library function +FUNC_STATICDEF= idaapi.FUNC_STATICDEF# static function +FUNC_FRAME = idaapi.FUNC_FRAME # function uses frame pointer (BP) +FUNC_USERFAR = idaapi.FUNC_USERFAR # user has specified far-ness + # of the function +FUNC_HIDDEN = idaapi.FUNC_HIDDEN # a hidden function +FUNC_THUNK = idaapi.FUNC_THUNK # thunk (jump) function +FUNC_BOTTOMBP = idaapi.FUNC_BOTTOMBP # BP points to the bottom of the stack frame + + +def SetFunctionFlags(ea, flags): + """ + Change function flags + + @param ea: any address belonging to the function + @param flags: see GetFunctionFlags() for explanations + + @return: !=0 - ok + """ + func = idaapi.get_func(ea) + + if not func: + return 0 + else: + func.flags = flags + idaapi.update_func(func) + return 1 + + +def GetFunctionName(ea): + """ + Retrieve function name + + @param ea: any address belonging to the function + + @return: null string - function doesn't exist + otherwise returns function name + """ + name = idaapi.get_func_name(ea) + + if not name: + return "" + else: + return name + + +def GetFunctionCmt(ea, repeatable): + """ + Retrieve function comment + + @param ea: any address belonging to the function + @param repeatable: 1: get repeatable comment + 0: get regular comment + + @return: function comment string + """ + func = idaapi.get_func(ea) + + if not func: + return "" + else: + comment = idaapi.get_func_cmt(func, repeatable) + + if not comment: + return "" + else: + return comment + + +def SetFunctionCmt(ea, cmt, repeatable): + """ + Set function comment + + @param ea: any address belonging to the function + @param cmt: a function comment line + @param repeatable: 1: get repeatable comment + 0: get regular comment + """ + func = idaapi.get_func(ea) + + if not func: + return None + else: + return idaapi.set_func_cmt(func, cmt, repeatable) + + +def ChooseFunction(title): + """ + Ask the user to select a function + + Arguments: + + @param title: title of the dialog box + + @return: -1 - user refused to select a function + otherwise returns the selected function start address + """ + return idaapi.choose_func(title) + + +def GetFuncOffset(ea): + """ + Convert address to 'funcname+offset' string + + @param ea: address to convert + + @return: if the address belongs to a function then return a string + formed as 'name+offset' where 'name' is a function name + 'offset' is offset within the function else return null string + """ + return idaapi.a2funcoff(ea) + + +def FindFuncEnd(ea): + """ + Determine a new function boundaries + + @param ea: starting address of a new function + + @return: if a function already exists, then return its end address. + If a function end cannot be determined, the return BADADDR + otherwise return the end address of the new function + """ + func = idaapi.func_t() + + res = idaapi.find_func_bounds(ea, func, idaapi.FIND_FUNC_DEFINE) + + if res == idaapi.FIND_FUNC_UNDEF: + return BADADDR + else: + return func.endEA + + +def GetFrame(ea): + """ + Get ID of function frame structure + + @param ea: any address belonging to the function + + @return: ID of function frame or None In order to access stack variables + you need to use structure member manipulaion functions with the + obtained ID. + """ + frame = idaapi.get_frame(ea) + + if frame: + return frame.id + else: + return None + + +def GetFrameLvarSize(ea): + """ + Get size of local variables in function frame + + @param ea: any address belonging to the function + + @return: Size of local variables in bytes. + If the function doesn't have a frame, return 0 + If the function does't exist, return None + """ + return GetFunctionAttr(ea, FUNCATTR_FRSIZE) + + +def GetFrameRegsSize(ea): + """ + Get size of saved registers in function frame + + @param ea: any address belonging to the function + + @return: Size of saved registers in bytes. + If the function doesn't have a frame, return 0 + This value is used as offset for BP (if FUNC_FRAME is set) + If the function does't exist, return None + """ + return GetFunctionAttr(ea, FUNCATTR_FRREGS) + + +def GetFrameArgsSize(ea): + """ + Get size of arguments in function frame which are purged upon return + + @param ea: any address belonging to the function + + @return: Size of function arguments in bytes. + If the function doesn't have a frame, return 0 + If the function does't exist, return -1 + """ + return GetFunctionAttr(ea, FUNCATTR_ARGSIZE) + + +def GetFrameSize(ea): + """ + Get full size of function frame + + @param ea: any address belonging to the function + @returns: Size of function frame in bytes. + This function takes into account size of local + variables + size of saved registers + size of + return address + size of function arguments + If the function doesn't have a frame, return size of + function return address in the stack. + If the function does't exist, return 0 + """ + func = idaapi.get_func(ea) + + if not func: + return 0 + else: + return idaapi.get_frame_size(func) + + +def MakeFrame(ea, lvsize, frregs, argsize): + """ + Make function frame + + @param ea: any address belonging to the function + @param lvsize: size of function local variables + @param frregs: size of saved registers + @param argsize: size of function arguments + + @return: ID of function frame or -1 + If the function did not have a frame, the frame + will be created. Otherwise the frame will be modified + """ + func = idaapi.get_func(ea) + + if not func: + return -1 + + frameid = idaapi.add_frame(func, lvsize, frregs, argsize) + + if not frameid: + if not idaapi.set_frame_size(func, lvsize, frregs, argsize): + return -1 + + return func.frame + + +def GetSpd(ea): + """ + Get current delta for the stack pointer + + @param ea: end address of the instruction + i.e.the last address of the instruction+1 + + @return: The difference between the original SP upon + entering the function and SP for the specified address + """ + func = idaapi.get_func(ea) + + if not func: + return None + + return idaapi.get_spd(func, ea) + + +def GetSpDiff(ea): + """ + Get modification of SP made by the instruction + + @param ea: end address of the instruction + i.e.the last address of the instruction+1 + + @return: Get modification of SP made at the specified location + If the specified location doesn't contain a SP change point, return 0 + Otherwise return delta of SP modification + """ + func = idaapi.get_func(ea) + + if not func: + return None + + return idaapi.get_sp_delta(func, ea) + + +def SetSpDiff(ea, delta): + """ + Setup modification of SP made by the instruction + + @param ea: end address of the instruction + i.e.the last address of the instruction+1 + @param delta: the difference made by the current instruction. + + @return: 1-ok, 0-failed + """ + return idaapi.add_user_stkpnt(ea, delta) + + +# ---------------------------------------------------------------------------- +# E N T R Y P O I N T S +# ---------------------------------------------------------------------------- + +def GetEntryPointQty(): + """ + Retrieve number of entry points + + @returns: number of entry points + """ + return idaapi.get_entry_qty() + + +def AddEntryPoint(ordinal, ea, name, makecode): + """ + Add entry point + + @param ordinal: entry point number + if entry point doesn't have an ordinal + number, 'ordinal' should be equal to 'ea' + @param ea: address of the entry point + @param name: name of the entry point. If null string, + the entry point won't be renamed. + @param makecode: if 1 then this entry point is a start + of a function. Otherwise it denotes data bytes. + + @return: 0 - entry point with the specifed ordinal already exists + 1 - ok + """ + return idaapi.add_entry(ordinal, ea, name, makecode) + + +def GetEntryOrdinal(index): + """ + Retrieve entry point ordinal number + + @param index: 0..GetEntryPointQty()-1 + + @return: 0 if entry point doesn't exist + otherwise entry point ordinal + """ + return idaapi.get_entry_ordinal(index) + + +def GetEntryPoint(ordinal): + """ + Retrieve entry point address + + @param ordinal: entry point number + it is returned by GetEntryPointOrdinal() + + @return: -1 if entry point doesn't exist + otherwise entry point address. + If entry point address is equal to its ordinal + number, then the entry point has no ordinal. + """ + return idaapi.get_entry(ordinal) + + +def GetEntryName(ordinal): + """ + Retrieve entry point name + + @param ordinal: entry point number, ass returned by GetEntryPointOrdinal() + + @return: entry point name or None + """ + return idaapi.get_entry_name(ordinal) + + +def RenameEntryPoint(ordinal, name): + """ + Rename entry point + + @param ordinal: entry point number + @param name: new name + + @return: !=0 - ok + """ + return idaapi.rename_entry(ordinal, name) + + +# ---------------------------------------------------------------------------- +# F I X U P S +# ---------------------------------------------------------------------------- +def GetNextFixupEA(ea): + """ + Find next address with fixup information + + @param ea: current address + + @return: -1 - no more fixups otherwise returns the next + address with fixup information + """ + return idaapi.get_next_fixup_ea(ea) + + +def GetPrevFixupEA(ea): + """ + Find previous address with fixup information + + @param ea: current address + + @return: -1 - no more fixups otherwise returns the + previous address with fixup information + """ + return idaapi.get_prev_fixup_ea(ea) + + +def GetFixupTgtType(ea): + """ + Get fixup target type + + @param ea: address to get information about + + @return: -1 - no fixup at the specified address + otherwise returns fixup target type: + """ + fd = idaapi.get_fixup(ea) + + if not fd: + return -1 + + return fd.type + + +FIXUP_MASK = 0xF +FIXUP_OFF8 = 0 # 8-bit offset. +FIXUP_BYTE = FIXUP_OFF8 # 8-bit offset. +FIXUP_OFF16 = 1 # 16-bit offset. +FIXUP_SEG16 = 2 # 16-bit base--logical segment base (selector). +FIXUP_PTR32 = 3 # 32-bit long pointer (16-bit base:16-bit + # offset). +FIXUP_OFF32 = 4 # 32-bit offset. +FIXUP_PTR48 = 5 # 48-bit pointer (16-bit base:32-bit offset). +FIXUP_HI8 = 6 # high 8 bits of 16bit offset +FIXUP_HI16 = 7 # high 16 bits of 32bit offset +FIXUP_LOW8 = 8 # low 8 bits of 16bit offset +FIXUP_LOW16 = 9 # low 16 bits of 32bit offset +FIXUP_REL = 0x10 # fixup is relative to the linear address + # specified in the 3d parameter to set_fixup() +FIXUP_SELFREL = 0x0 # self-relative? + # - disallows the kernel to convert operands + # in the first pass + # - this fixup is used during output + # This type of fixups is not used anymore. + # Anyway you can use it for commenting purposes + # in the loader modules +FIXUP_EXTDEF = 0x20 # target is a location (otherwise - segment) +FIXUP_UNUSED = 0x40 # fixup is ignored by IDA + # - disallows the kernel to convert operands + # - this fixup is not used during output +FIXUP_CREATED = 0x80 # fixup was not present in the input file + + +def GetFixupTgtSel(ea): + """ + Get fixup target selector + + @param ea: address to get information about + + @return: -1 - no fixup at the specified address + otherwise returns fixup target selector + """ + fd = idaapi.get_fixup(ea) + + if not fd: + return -1 + + return fd.sel + + +def GetFixupTgtOff(ea): + """ + Get fixup target offset + + @param ea: address to get information about + + @return: -1 - no fixup at the specified address + otherwise returns fixup target offset + """ + fd = idaapi.get_fixup(ea) + + if not fd: + return -1 + + return fd.off + + +def GetFixupTgtDispl(ea): + """ + Get fixup target displacement + + @param ea: address to get information about + + @return: -1 - no fixup at the specified address + otherwise returns fixup target displacement + """ + fd = idaapi.get_fixup(ea) + + if not fd: + return -1 + + return fd.displacement + + +def SetFixup(ea, fixuptype, targetsel, targetoff, displ): + """ + Set fixup information + + @param ea: address to set fixup information about + @param fixuptype: fixup type. see GetFixupTgtType() + for possible fixup types. + @param targetsel: target selector + @param targetoff: target offset + @param displ: displacement + + @return: none + """ + fd = idaapi.fixup_data_t() + fd.type = fixuptype + fd.sel = targetsel + fd.off = targetoff + fd.displacement = displ + + idaapi.set_fixup(ea, fd) + + +def DelFixup(ea): + """ + Delete fixup information + + @param ea: address to delete fixup information about + + @return: None + """ + idaapi.del_fixup(ea) + + +#---------------------------------------------------------------------------- +# M A R K E D P O S I T I O N S +#---------------------------------------------------------------------------- + +def MarkPosition(ea, lnnum, x, y, slot, comment): + """ + Mark position + + @param ea: address to mark + @param lnnum: number of generated line for the 'ea' + @param x: x coordinate of cursor + @param y: y coordinate of cursor + @param slot: slot number: 1..1024 + if the specifed value is not within the + range, IDA will ask the user to select slot. + @param comment: description of the mark. Should be not empty. + + @return: None + """ + curloc = idaapi.curloc() + curloc.ea = ea + curloc.lnnum = lnnum + curloc.x = x + curloc.y = y + curloc.mark(slot, comment, comment) + + +def GetMarkedPos(slot): + """ + Get marked position + + @param slot: slot number: 1..1024 if the specifed value is <= 0 + range, IDA will ask the user to select slot. + + @return: BADADDR - the slot doesn't contain a marked address + otherwise returns the marked address + """ + curloc = idaapi.curloc() + intp = idaapi.int_pointer() + intp.assign(slot) + return curloc.markedpos(intp) + + +def GetMarkComment(slot): + """ + Get marked position comment + + @param slot: slot number: 1..1024 + + @return: None if the slot doesn't contain a marked address + otherwise returns the marked address comment + """ + curloc = idaapi.curloc() + return curloc.markdesc(slot) + + +# ---------------------------------------------------------------------------- +# S T R U C T U R E S +# ---------------------------------------------------------------------------- + +def GetStrucQty(): + """ + Get number of defined structure types + + @return: number of structure types + """ + return idaapi.get_struc_qty() + + +def GetFirstStrucIdx(): + """ + Get index of first structure type + + @return: -1 if no structure type is defined + index of first structure type. + Each structure type has an index and ID. + INDEX determines position of structure definition + in the list of structure definitions. Index 1 + is listed first, after index 2 and so on. + The index of a structure type can be changed any + time, leading to movement of the structure definition + in the list of structure definitions. + ID uniquely denotes a structure type. A structure + gets a unique ID at the creation time and this ID + can't be changed. Even when the structure type gets + deleted, its ID won't be resued in the future. + """ + return idaapi.get_first_struc_idx() + + +def GetLastStrucIdx(): + """ + Get index of last structure type + + @return: -1 if no structure type is defined + index of last structure type. + See GetFirstStrucIdx() for the explanation of + structure indices and IDs. + """ + return idaapi.get_last_struc_idx() + + +def GetNextStrucIdx(index): + """ + Get index of next structure type + + @param index: current structure index + + @return: -1 if no (more) structure type is defined + index of the next structure type. + See GetFirstStrucIdx() for the explanation of + structure indices and IDs. + """ + return idaapi.get_next_struc_idx(index) + + +def GetPrevStrucIdx(index): + """ + Get index of previous structure type + + @param index: current structure index + + @return: -1 if no (more) structure type is defined + index of the presiouvs structure type. + See GetFirstStrucIdx() for the explanation of + structure indices and IDs. + """ + return idaapi.get_prev_struc_idx(index) + + +def GetStrucIdx(sid): + """ + Get structure index by structure ID + + @param sid: structure ID + + @return: -1 if bad structure ID is passed + otherwise returns structure index. + See GetFirstStrucIdx() for the explanation of + structure indices and IDs. + """ + return idaapi.get_struc_idx(sid) + + +def GetStrucId(index): + """ + Get structure ID by structure index + + @param index: structure index + + @return: -1 if bad structure index is passed otherwise returns structure ID. + + @note: See GetFirstStrucIdx() for the explanation of structure indices and IDs. + """ + return idaapi.get_struc_by_idx(index) + + +def GetStrucIdByName(name): + """ + Get structure ID by structure name + + @param name: structure type name + + @return: -1 if bad structure type name is passed + otherwise returns structure ID. + """ + return idaapi.get_struc_id(name) + + +def GetStrucName(sid): + """ + Get structure type name + + @param sid: structure type ID + + @return: -1 if bad structure type ID is passed + otherwise returns structure type name. + """ + return idaapi.get_struc_name(sid) + + +def GetStrucComment(sid, repeatable): + """ + Get structure type comment + + @param sid: structure type ID + @param repeatable: 1: get repeatable comment + 0: get regular comment + + @return: None if bad structure type ID is passed + otherwise returns comment. + """ + return idaapi.get_struc_cmt(sid, repeatable) + + +def GetStrucSize(sid): + """ + Get size of a structure + + @param sid: structure type ID + + @return: -1 if bad structure type ID is passed + otherwise returns size of structure in bytes. + """ + return idaapi.get_struc_size(sid) + + +def GetMemberQty(sid): + """ + Get number of members of a structure + + @param sid: structure type ID + + @return: -1 if bad structure type ID is passed otherwise + returns number of members. + """ + s = idaapi.get_struc(sid) + if not s: + return -1 + + return s.memqty + + +def GetStrucPrevOff(sid, offset): + """ + Get previous offset in a structure + + @param sid: structure type ID + @param offset: current offset + + @return: -1 if bad structure type ID is passed + or no (more) offsets in the structure + otherwise returns previous offset in a structure. + + @note: IDA allows 'holes' between members of a + structure. It treats these 'holes' + as unnamed arrays of bytes. + This function returns a member offset or a hole offset. + It will return size of the structure if input + 'offset' is bigger than the structure size. + """ + s = idaapi.get_struc(sid) + if not s: + return -1 + + return idaapi.get_struc_prev_offset(s, offset) + + +def GetStrucNextOff(sid, offset): + """ + Get next offset in a structure + + @param sid: structure type ID + @param offset: current offset + + @return: -1 if bad structure type ID is passed + or no (more) offsets in the structure + otherwise returns next offset in a structure. + + @note: IDA allows 'holes' between members of a + structure. It treats these 'holes' + as unnamed arrays of bytes. + This function returns a member offset or a hole offset. + It will return size of the structure if input + 'offset' belongs to the last member of the structure. + """ + s = idaapi.get_struc(sid) + if not s: + return -1 + + return idaapi.get_struc_next_offset(s, offset) + + +def GetFirstMember(sid): + """ + Get offset of the first member of a structure + + @param sid: structure type ID + + @return: -1 if bad structure type ID is passed + or structure has no members + otherwise returns offset of the first member. + + @note: IDA allows 'holes' between members of a + structure. It treats these 'holes' + as unnamed arrays of bytes. + """ + s = idaapi.get_struc(sid) + if not s: + return -1 + + return idaapi.get_struc_first_offset(s) + + +def GetLastMember(sid): + """ + Get offset of the last member of a structure + + @param sid: structure type ID + + @return: -1 if bad structure type ID is passed + or structure has no members + otherwise returns offset of the last member. + + @note: IDA allows 'holes' between members of a + structure. It treats these 'holes' + as unnamed arrays of bytes. + """ + s = idaapi.get_struc(sid) + if not s: + return -1 + + return idaapi.get_struc_last_offset(s) + + +def GetMemberOffset(sid, member_name): + """ + Get offset of a member of a structure by the member name + + @param sid: structure type ID + @param member_name: name of structure member + + @return: -1 if bad structure type ID is passed + or no such member in the structure + otherwise returns offset of the specified member. + """ + s = idaapi.get_struc(sid) + if not s: + return -1 + + m = idaapi.get_member_by_name(s, member_name) + if not m: + return -1 + + return m.get_soff() + + +def GetMemberName(sid, member_offset): + """ + Get name of a member of a structure + + @param sid: structure type ID + @param member_offset: member offset. The offset can be + any offset in the member. For example, + is a member is 4 bytes long and starts + at offset 2, then 2,3,4,5 denote + the same structure member. + + @return: None if bad structure type ID is passed + or no such member in the structure + otherwise returns name of the specified member. + """ + s = idaapi.get_struc(sid) + if not s: + return None + + m = idaapi.get_member(s, member_offset) + if not m: + return None + + return idaapi.get_member_name(m.id) + + +def GetMemberComment(sid, member_offset, repeatable): + """ + Get comment of a member + + @param sid: structure type ID + @param member_offset: member offset. The offset can be + any offset in the member. For example, + is a member is 4 bytes long and starts + at offset 2, then 2,3,4,5 denote + the same structure member. + @param repeatable: 1: get repeatable comment + 0: get regular comment + + @return: None if bad structure type ID is passed + or no such member in the structure + otherwise returns comment of the specified member. + """ + s = idaapi.get_struc(sid) + if not s: + return None + + m = idaapi.get_member(s, member_offset) + if not m: + return None + + return idaapi.get_member_cmt(m.id, repeatable) + + +def GetMemberSize(sid, member_offset): + """ + Get size of a member + + @param sid: structure type ID + @param member_offset: member offset. The offset can be + any offset in the member. For example, + is a member is 4 bytes long and starts + at offset 2, then 2,3,4,5 denote + the same structure member. + + @return: -1 if bad structure type ID is passed + or no such member in the structure + otherwise returns size of the specified + member in bytes. + """ + s = idaapi.get_struc(sid) + if not s: + return None + + m = idaapi.get_member(s, member_offset) + if not m: + return None + + return idaapi.get_member_size(m) + + +def GetMemberFlag(sid, member_offset): + """ + Get type of a member + + @param sid: structure type ID + @param member_offset: member offset. The offset can be + any offset in the member. For example, + is a member is 4 bytes long and starts + at offset 2, then 2,3,4,5 denote + the same structure member. + + @return: -1 if bad structure type ID is passed + or no such member in the structure + otherwise returns type of the member, see bit + definitions above. If the member type is a structure + then function GetMemberStrid() should be used to + get the structure type id. + """ + s = idaapi.get_struc(sid) + if not s: + return -1 + + m = idaapi.get_member(s, member_offset) + if not m: + return -1 + + return m.flag + + +def GetMemberStrId(sid, member_offset): + """ + Get structure id of a member + + @param sid: structure type ID + @param member_offset: member offset. The offset can be + any offset in the member. For example, + is a member is 4 bytes long and starts + at offset 2, then 2,3,4,5 denote + the same structure member. + @return: -1 if bad structure type ID is passed + or no such member in the structure + otherwise returns structure id of the member. + If the current member is not a structure, returns -1. + """ + s = idaapi.get_struc(sid) + if not s: + return -1 + + m = idaapi.get_member(s, member_offset) + if not m: + return -1 + + cs = idaapi.get_sptr(m) + if cs: + return cs.id + else: + return -1 + + +def IsUnion(sid): + """ + Is a structure a union? + + @param sid: structure type ID + + @return: 1: yes, this is a union id + 0: no + + @note: Unions are a special kind of structures + """ + s = idaapi.get_struc(sid) + if not s: + return 0 + + return s.is_union() + + +def AddStrucEx(index, name, is_union): + """ + Define a new structure type + + @param index: index of new structure type + If another structure has the specified index, + then index of that structure and all other + structures will be incremented, freeing the specifed + index. If index is == -1, then the biggest index + number will be used. + See GetFirstStrucIdx() for the explanation of + structure indices and IDs. + @param name: name of the new structure type. + @param is_union: 0: structure + 1: union + + @return: -1 if can't define structure type because of + bad structure name: the name is ill-formed or is + already used in the program. + otherwise returns ID of the new structure type + """ + if index == -1: + index = BADADDR + + return idaapi.add_struc(index, name, is_union) + + +def DelStruc(sid): + """ + Delete a structure type + + @param sid: structure type ID + + @return: 0 if bad structure type ID is passed + 1 otherwise the structure type is deleted. All data + and other structure types referencing to the + deleted structure type will be displayed as array + of bytes. + """ + s = idaapi.get_struc(sid) + if not s: + return 0 + + return idaapi.del_struc(s) + + +def SetStrucIdx(sid, index): + """ + Change structure index + + @param sid: structure type ID + @param index: new index of the structure + + @return: != 0 - ok + + @note: See GetFirstStrucIdx() for the explanation of + structure indices and IDs. + """ + s = idaapi.get_struc(sid) + if not s: + return 0 + + return idaapi.set_struc_idx(s, index) + + +def SetStrucName(sid, name): + """ + Change structure name + + @param sid: structure type ID + @param name: new name of the structure + + @return: != 0 - ok + """ + return idaapi.set_struc_name(sid, name) + + +def SetStrucComment(sid, comment, repeatable): + """ + Change structure comment + + @param sid: structure type ID + @param comment: new comment of the structure + @param repeatable: 1: change repeatable comment + 0: change regular comment + @return: != 0 - ok + """ + return idaapi.set_struc_cmt(sid, comment, repeatable) + + +def AddStrucMember(sid, name, offset, flag, typeid, nbytes, target=-1, tdelta=0, reftype=REF_OFF32): + """ + Add structure member + + @param sid: structure type ID + @param name: name of the new member + @param offset: offset of the new member + -1 means to add at the end of the structure + @param flag: type of the new member. Should be one of + FF_BYTE..FF_PACKREAL (see above) combined with FF_DATA + @param typeid: if isStruc(flag) then typeid specifies + the structure id for the member + if isOff0(flag) then typeid specifies the offset base. + if isASCII(flag) then typeid specifies the string type (ASCSTR_...). + if isStroff(flag) then typeid specifies the structure id + Otherwise should be -1. + @param nbytes: number of bytes in the new member + + @param target: target address of the offset expr. You may specify it as + -1, ida will calculate it itself + @param tdelta: offset target delta. usually 0 + @param reftype: see REF_... definitions + + @note: The remaining arguments are allowed only if isOff0(flag) and you want + to specify a complex offset expression + + @return: 0 - ok, otherwise error code (one of STRUC_ERROR_*) + + """ + if isOff0(flag): + return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d, %d, %d, %d);' % (sid, name, offset, flag, typeid, nbytes, + target, tdelta, reftype)) + else: + return Eval('AddStrucMember(%d, "%s", %d, %d, %d, %d);' % (sid, name, offset, flag, typeid, nbytes)) + + +STRUC_ERROR_MEMBER_NAME = -1 # already has member with this name (bad name) +STRUC_ERROR_MEMBER_OFFSET = -2 # already has member at this offset +STRUC_ERROR_MEMBER_SIZE = -3 # bad number of bytes or bad sizeof(type) +STRUC_ERROR_MEMBER_TINFO = -4 # bad typeid parameter +STRUC_ERROR_MEMBER_STRUCT = -5 # bad struct id (the 1st argument) +STRUC_ERROR_MEMBER_UNIVAR = -6 # unions can't have variable sized members +STRUC_ERROR_MEMBER_VARLAST = -7 # variable sized member should be the last member in the structure + + +def DelStrucMember(sid, member_offset): + """ + Delete structure member + + @param sid: structure type ID + @param member_offset: offset of the member + + @return: != 0 - ok. + + @note: IDA allows 'holes' between members of a + structure. It treats these 'holes' + as unnamed arrays of bytes. + """ + s = idaapi.get_struc(sid) + if not s: + return 0 + + return idaapi.del_struc_member(s, member_offset) + + +def SetMemberName(sid, member_offset, name): + """ + Change structure member name + + @param sid: structure type ID + @param member_offset: offset of the member + @param name: new name of the member + + @return: != 0 - ok. + """ + s = idaapi.get_struc(sid) + if not s: + return 0 + + return idaapi.set_member_name(s, member_offset, name) + + +def SetMemberType(sid, member_offset, flag, typeid, nitems, target=-1, tdelta=0, reftype=REF_OFF32): + """ + Change structure member type + + @param sid: structure type ID + @param member_offset: offset of the member + @param flag: new type of the member. Should be one of + FF_BYTE..FF_PACKREAL (see above) combined with FF_DATA + @param typeid: structure id if 'flag' == FF_STRU + Denotes type of the member is the member + itself is a structure. Otherwise should be -1. + if isOff0(flag) then typeid specifies the offset base. + if isASCII(flag) then typeid specifies the string type + (ASCSTR_...). + @param nitems: number of items in the member + + @param target: target address of the offset expr. You may specify it as + -1, ida will calculate it itself + @param tdelta: offset target delta. usually 0 + @param reftype: see REF_... definitions + + @note: The remaining arguments are allowed only if isOff0(flag) and you want + to specify a complex offset expression + + @return: !=0 - ok. + """ + if isOff0(flag): + return Eval('SetMemberType(%d, %d, %d, %d, %d, %d, %d, %d);' % (sid, member_offset, flag, typeid, nitems, + target, tdelta, reftype)) + else: + return Eval('SetMemberType(%d, %d, %d, %d, %d);' % (sid, member_offset, flag, typeid, nitems)) + + +def SetMemberComment(sid, member_offset, comment, repeatable): + """ + Change structure member comment + + @param sid: structure type ID + @param member_offset: offset of the member + @param comment: new comment of the structure member + @param repeatable: 1: change repeatable comment + 0: change regular comment + + @return: != 0 - ok + """ + s = idaapi.get_struc(sid) + if not s: + return 0 + + m = idaapi.get_member(s, member_offset) + if not m: + return 0 + + return idaapi.set_member_cmt(m, comment, repeatable) + + +def GetFchunkAttr(ea, attr): + """ + Get a function chunk attribute + + @param ea: any address in the chunk + @param attr: one of: FUNCATTR_START, FUNCATTR_END, FUNCATTR_OWNER, FUNCATTR_REFQTY + + @return: desired attribute or -1 + """ + return Eval("GetFchunkAttr(0x%x, %d);" % (ea, attr)) + + +def SetFchunkAttr(ea, attr, value): + """ + Set a function chunk attribute + + @param ea: any address in the chunk + @param attr: only FUNCATTR_START, FUNCATTR_END, FUNCATTR_OWNER + @param value: desired value + + @return: 0 if failed, 1 if success + """ + if attr in [ FUNCATTR_START, FUNCATTR_END, FUNCATTR_OWNER ]: + chunk = idaapi.get_fchunk(ea) + if chunk: + _IDC_SetAttr(chunk, _FUNCATTRMAP, attr, value) + return idaapi.update_func(chunk) + return 0 + + +def GetFchunkReferer(ea, idx): + """ + Get a function chunk referer + + @param ea: any address in the chunk + @param idx: referer index (0..GetFchunkAttr(FUNCATTR_REFQTY)) + + @return: referer address or BADADDR + """ + return idaapi.get_fchunk_referer(ea, idx) + + +def NextFchunk(ea): + """ + Get next function chunk + + @param ea: any address + + @return: the starting address of the next function chunk or BADADDR + + @note: This function enumerates all chunks of all functions in the database + """ + func = idaapi.get_next_fchunk(ea) + + if func: + return func.startEA + else: + return BADADDR + + +def PrevFchunk(ea): + """ + Get previous function chunk + + @param ea: any address + + @return: the starting address of the function chunk or BADADDR + + @note: This function enumerates all chunks of all functions in the database + """ + func = idaapi.get_prev_fchunk(ea) + + if func: + return func.startEA + else: + return BADADDR + + +def AppendFchunk(funcea, ea1, ea2): + """ + Append a function chunk to the function + + @param funcea: any address in the function + @param ea1: start of function tail + @param ea2: end of function tail + @return: 0 if failed, 1 if success + + @note: If a chunk exists at the specified addresses, it must have exactly + the specified boundaries + """ + func = idaapi.get_func(funcea) + + if not func: + return 0 + else: + return idaapi.append_func_tail(func, ea1, ea2) + + +def RemoveFchunk(funcea, tailea): + """ + Remove a function chunk from the function + + @param funcea: any address in the function + @param tailea: any address in the function chunk to remove + + @return: 0 if failed, 1 if success + """ + func = idaapi.get_func(funcea) + + if not func: + return 0 + else: + return idaapi.remove_func_tail(func, tailea) + + +def SetFchunkOwner(tailea, funcea): + """ + Change the function chunk owner + + @param tailea: any address in the function chunk + @param funcea: the starting address of the new owner + + @return: 0 if failed, 1 if success + + @note: The new owner must already have the chunk appended before the call + """ + tail = idaapi.get_func(tailea) + + if not tail: + return 0 + else: + return idaapi.set_tail_owner(tail, funcea) + + +def FirstFuncFchunk(funcea): + """ + Get the first function chunk of the specified function + + @param funcea: any address in the function + + @return: the function entry point or BADADDR + + @note: This function returns the first (main) chunk of the specified function + """ + func = idaapi.get_func(funcea) + fci = idaapi.func_tail_iterator_t(func, funcea) + if fci.main(): + return fci.chunk().startEA + else: + return BADADDR + + +def NextFuncFchunk(funcea, tailea): + """ + Get the next function chunk of the specified function + + @param funcea: any address in the function + @param tailea: any address in the current chunk + + @return: the starting address of the next function chunk or BADADDR + + @note: This function returns the next chunk of the specified function + """ + func = idaapi.get_func(funcea) + fci = idaapi.func_tail_iterator_t(func, funcea) + if not fci.main(): + return BADADDR + + # Iterate and try to find the current chunk + found = False + while True: + if fci.chunk().startEA <= tailea and \ + fci.chunk().endEA > tailea: + found = True + break + if not fci.next(): + break + + # Return the next chunk, if there is one + if found and fci.next(): + return fci.chunk().startEA + else: + return BADADDR + + +# ---------------------------------------------------------------------------- +# E N U M S +# ---------------------------------------------------------------------------- +def GetEnumQty(): + """ + Get number of enum types + + @return: number of enumerations + """ + return idaapi.get_enum_qty() + + +def GetnEnum(idx): + """ + Get ID of the specified enum by its serial number + + @param idx: number of enum (0..GetEnumQty()-1) + + @return: ID of enum or -1 if error + """ + return idaapi.getn_enum(idx) + + +def GetEnumIdx(enum_id): + """ + Get serial number of enum by its ID + + @param enum_id: ID of enum + + @return: (0..GetEnumQty()-1) or -1 if error + """ + return idaapi.get_enum_idx(enum_id) + + +def GetEnum(name): + """ + Get enum ID by the name of enum + + Arguments: + name - name of enum + + returns: ID of enum or -1 if no such enum exists + """ + return idaapi.get_enum(name) + + +def GetEnumName(enum_id): + """ + Get name of enum + + @param enum_id: ID of enum + + @return: name of enum or empty string + """ + return idaapi.get_enum_name(enum_id) + + +def GetEnumCmt(enum_id, repeatable): + """ + Get comment of enum + + @param enum_id: ID of enum + @param repeatable: 0:get regular comment + 1:get repeatable comment + + @return: comment of enum + """ + return idaapi.get_enum_cmt(enum_id, repeatable) + + +def GetEnumSize(enum_id): + """ + Get size of enum + + @param enum_id: ID of enum + + @return: number of constants in the enum + Returns 0 if enum_id is bad. + """ + return idaapi.get_enum_size(enum_id) + + +def GetEnumWidth(enum_id): + """ + Get width of enum elements + + @param enum_id: ID of enum + + @return: log2(size of enum elements in bytes)+1 + possible returned values are 1..7 + 1-1byte,2-2bytes,3-4bytes,4-8bytes,etc + Returns 0 if enum_id is bad or the width is unknown. + """ + return idaapi.get_enum_width(enum_id) + + +def GetEnumFlag(enum_id): + """ + Get flag of enum + + @param enum_id: ID of enum + + @return: flags of enum. These flags determine representation + of numeric constants (binary,octal,decimal,hex) + in the enum definition. See start of this file for + more information about flags. + Returns 0 if enum_id is bad. + """ + return idaapi.get_enum_flag(enum_id) + + +def GetConstByName(name): + """ + Get member of enum - a symbolic constant ID + + @param name: name of symbolic constant + + @return: ID of constant or -1 + """ + return idaapi.get_enum_member_by_name(name) + + +def GetConstValue(const_id): + """ + Get value of symbolic constant + + @param const_id: id of symbolic constant + + @return: value of constant or 0 + """ + return idaapi.get_enum_member_value(const_id) + + +def GetConstBmask(const_id): + """ + Get bit mask of symbolic constant + + @param const_id: id of symbolic constant + + @return: bitmask of constant or 0 + ordinary enums have bitmask = -1 + """ + return idaapi.get_enum_member_bmask(const_id) + + +def GetConstEnum(const_id): + """ + Get id of enum by id of constant + + @param const_id: id of symbolic constant + + @return: id of enum the constant belongs to. + -1 if const_id is bad. + """ + return idaapi.get_enum_member_enum(const_id) + + +def GetConstEx(enum_id, value, serial, bmask): + """ + Get id of constant + + @param enum_id: id of enum + @param value: value of constant + @param serial: serial number of the constant in the + enumeration. See OpEnumEx() for details. + @param bmask: bitmask of the constant + ordinary enums accept only -1 as a bitmask + + @return: id of constant or -1 if error + """ + return idaapi.get_enum_member(enum_id, value, serial, bmask) + + +def GetFirstBmask(enum_id): + """ + Get first bitmask in the enum (bitfield) + + @param enum_id: id of enum (bitfield) + + @return: the smallest bitmask of constant or -1 + no bitmasks are defined yet + All bitmasks are sorted by their values + as unsigned longs. + """ + return idaapi.get_first_bmask(enum_id) + + +def GetLastBmask(enum_id): + """ + Get last bitmask in the enum (bitfield) + + @param enum_id: id of enum + + @return: the biggest bitmask or -1 no bitmasks are defined yet + All bitmasks are sorted by their values as unsigned longs. + """ + return idaapi.get_last_bmask(enum_id) + + +def GetNextBmask(enum_id, value): + """ + Get next bitmask in the enum (bitfield) + + @param enum_id: id of enum + @param value: value of the current bitmask + + @return: value of a bitmask with value higher than the specified + value. -1 if no such bitmasks exist. + All bitmasks are sorted by their values + as unsigned longs. + """ + return idaapi.get_next_bmask(enum_id, value) + + +def GetPrevBmask(enum_id, value): + """ + Get prev bitmask in the enum (bitfield) + + @param enum_id: id of enum + @param value: value of the current bitmask + + @return: value of a bitmask with value lower than the specified + value. -1 no such bitmasks exist. + All bitmasks are sorted by their values as unsigned longs. + """ + return idaapi.get_prev_bmask(enum_id, value) + + +def GetBmaskName(enum_id, bmask): + """ + Get bitmask name (only for bitfields) + + @param enum_id: id of enum + @param bmask: bitmask of the constant + + @return: name of bitmask or None + """ + return idaapi.get_bmask_name(enum_id, bmask) + + +def GetBmaskCmt(enum_id, bmask, repeatable): + """ + Get bitmask comment (only for bitfields) + + @param enum_id: id of enum + @param bmask: bitmask of the constant + @param repeatable: type of comment, 0-regular, 1-repeatable + + @return: comment attached to bitmask or None + """ + return idaapi.get_bmask_cmt(enum_id, bmask, repeatable) + + +def SetBmaskName(enum_id, bmask, name): + """ + Set bitmask name (only for bitfields) + + @param enum_id: id of enum + @param bmask: bitmask of the constant + @param name: name of bitmask + + @return: 1-ok, 0-failed + """ + return idaapi.set_bmask_name(enum_id, bmask, name) + + +def SetBmaskCmt(enum_id, bmask, cmt, repeatable): + """ + Set bitmask comment (only for bitfields) + + @param enum_id: id of enum + @param bmask: bitmask of the constant + @param cmt: comment + repeatable - type of comment, 0-regular, 1-repeatable + + @return: 1-ok, 0-failed + """ + return idaapi.set_bmask_cmt(enum_id, bmask, cmt, repeatable) + + +def GetFirstConst(enum_id, bmask): + """ + Get first constant in the enum + + @param enum_id: id of enum + @param bmask: bitmask of the constant (ordinary enums accept only -1 as a bitmask) + + @return: value of constant or -1 no constants are defined + All constants are sorted by their values as unsigned longs. + """ + return idaapi.get_first_enum_member(enum_id, bmask) + + +def GetLastConst(enum_id, bmask): + """ + Get last constant in the enum + + @param enum_id: id of enum + @param bmask: bitmask of the constant (ordinary enums accept only -1 as a bitmask) + + @return: value of constant or -1 no constants are defined + All constants are sorted by their values + as unsigned longs. + """ + return idaapi.get_last_enum_member(enum_id, bmask) + + +def GetNextConst(enum_id, value, bmask): + """ + Get next constant in the enum + + @param enum_id: id of enum + @param bmask: bitmask of the constant ordinary enums accept only -1 as a bitmask + @param value: value of the current constant + + @return: value of a constant with value higher than the specified + value. -1 no such constants exist. + All constants are sorted by their values as unsigned longs. + """ + return idaapi.get_next_enum_member(enum_id, value, bmask) + + +def GetPrevConst(enum_id, value, bmask): + """ + Get prev constant in the enum + + @param enum_id: id of enum + @param bmask : bitmask of the constant + ordinary enums accept only -1 as a bitmask + @param value: value of the current constant + + @return: value of a constant with value lower than the specified + value. -1 no such constants exist. + All constants are sorted by their values as unsigned longs. + """ + return idaapi.get_prev_enum_member(enum_id, value, bmask) + + +def GetConstName(const_id): + """ + Get name of a constant + + @param const_id: id of const + + Returns: name of constant + """ + name = idaapi.get_enum_member_name(const_id) + + if not name: + return "" + else: + return name + + +def GetConstCmt(const_id, repeatable): + """ + Get comment of a constant + + @param const_id: id of const + @param repeatable: 0:get regular comment, 1:get repeatable comment + + @return: comment string + """ + cmt = idaapi.get_enum_member_cmt(const_id, repeatable) + + if not cmt: + return "" + else: + return cmt + + +def AddEnum(idx, name, flag): + """ + Add a new enum type + + @param idx: serial number of the new enum. + If another enum with the same serial number + exists, then all enums with serial + numbers >= the specified idx get their + serial numbers incremented (in other words, + the new enum is put in the middle of the list of enums). + + If idx >= GetEnumQty() or idx == -1 + then the new enum is created at the end of + the list of enums. + @param name: name of the enum. + @param flag: flags for representation of numeric constants + in the definition of enum. + + @return: id of new enum or -1. + """ + return idaapi.add_enum(idx, name, flag) + + +def DelEnum(enum_id): + """ + Delete enum type + + @param enum_id: id of enum + + @return: None + """ + idaapi.del_enum(enum_id) + + +def SetEnumIdx(enum_id, idx): + """ + Give another serial number to a enum + + @param enum_id: id of enum + @param idx: new serial number. + If another enum with the same serial number + exists, then all enums with serial + numbers >= the specified idx get their + serial numbers incremented (in other words, + the new enum is put in the middle of the list of enums). + + If idx >= GetEnumQty() then the enum is + moved to the end of the list of enums. + + @return: comment string + """ + return idaapi.set_enum_idx(enum_id, idx) + + +def SetEnumName(enum_id, name): + """ + Rename enum + + @param enum_id: id of enum + @param name: new name of enum + + @return: 1-ok,0-failed + """ + return idaapi.set_enum_name(enum_id, name) + + +def SetEnumCmt(enum_id, cmt, repeatable): + """ + Set comment of enum + + @param enum_id: id of enum + @param cmt: new comment for the enum + @param repeatable: is the comment repeatable? + - 0:set regular comment + - 1:set repeatable comment + + @return: 1-ok,0-failed + """ + return idaapi.set_enum_cmt(enum_id, cmt, repeatable) + + +def SetEnumFlag(enum_id, flag): + """ + Set flag of enum + + @param enum_id: id of enum + @param flag: flags for representation of numeric constants + in the definition of enum. + + @return: 1-ok,0-failed + """ + return idaapi.set_enum_flag(enum_id, flag) + + +def SetEnumBf(enum_id, flag): + """ + Set bitfield property of enum + + @param enum_id: id of enum + @param flag: flags + - 1: convert to bitfield + - 0: convert to ordinary enum + + @return: 1-ok,0-failed + """ + return idaapi.set_enum_bf(enum_id, flag) + + +def SetEnumWidth(enum_id, width): + """ + Set width of enum elements + + @param enum_id: id of enum + @param width: element width in bytes + allowed values: 0-unknown + or 1..7: (log2 of the element size)+1 + + @return: 1-ok, 0-failed + """ + return idaapi.set_enum_width(enum_id, width) + + +def IsBitfield(enum_id): + """ + Is enum a bitfield? + + @param enum_id: id of enum + + @return: 1-yes, 0-no, ordinary enum + """ + return idaapi.is_bf(enum_id) + + +def AddConstEx(enum_id, name, value, bmask): + """ + Add a member of enum - a symbolic constant + + @param enum_id: id of enum + @param name: name of symbolic constant. Must be unique in the program. + @param value: value of symbolic constant. + @param bmask: bitmask of the constant + ordinary enums accept only -1 as a bitmask + all bits set in value should be set in bmask too + + @return: 0-ok, otherwise error code (one of ENUM_MEMBER_ERROR_*) + """ + return idaapi.add_enum_member(enum_id, name, value, bmask) + + +ENUM_MEMBER_ERROR_NAME = idaapi.ENUM_MEMBER_ERROR_NAME # already have member with this name (bad name) +ENUM_MEMBER_ERROR_VALUE = idaapi.ENUM_MEMBER_ERROR_VALUE # already have member with this value +ENUM_MEMBER_ERROR_ENUM = idaapi.ENUM_MEMBER_ERROR_ENUM # bad enum id +ENUM_MEMBER_ERROR_MASK = idaapi.ENUM_MEMBER_ERROR_MASK # bad bmask +ENUM_MEMBER_ERROR_ILLV = idaapi.ENUM_MEMBER_ERROR_ILLV # bad bmask and value combination (~bmask & value != 0) + + +def DelConstEx(enum_id, value, serial, bmask): + """ + Delete a member of enum - a symbolic constant + + @param enum_id: id of enum + @param value: value of symbolic constant. + @param serial: serial number of the constant in the + enumeration. See OpEnumEx() for for details. + @param bmask: bitmask of the constant ordinary enums accept + only -1 as a bitmask + + @return: 1-ok, 0-failed + """ + return idaapi.del_enum_member(enum_id, value, serial, bmask) + + +def SetConstName(const_id, name): + """ + Rename a member of enum - a symbolic constant + + @param const_id: id of const + @param name: new name of constant + + @return: 1-ok, 0-failed + """ + return idaapi.set_enum_member_name(const_id, name) + + +def SetConstCmt(const_id, cmt, repeatable): + """ + Set a comment of a symbolic constant + + @param const_id: id of const + @param cmt: new comment for the constant + @param repeatable: is the comment repeatable? + 0: set regular comment + 1: set repeatable comment + + @return: 1-ok, 0-failed + """ + return idaapi.set_enum_member_cmt(const_id, cmt, repeatable) + +#---------------------------------------------------------------------------- +# A R R A Y S I N I D C +#---------------------------------------------------------------------------- + +def CreateArray(name): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetArrayId(name): + raise DeprecatedIDCError, "Use python pickles instead." + +def RenameArray(hashid, newname): + raise DeprecatedIDCError, "Use python pickles instead." + +def DeleteArray(hashid): + raise DeprecatedIDCError, "Use python pickles instead." + +def SetArrayLong(hashid, idx, value): + raise DeprecatedIDCError, "Use python pickles instead." + +def SetArrayString(hashid, idx, s): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetArrayElement(tag, hashid, idx): + raise DeprecatedIDCError, "Use python pickles instead." + +def DelArrayElement(tag, hashid, idx): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetFirstIndex(tag, hashid): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetLastIndex(tag, hashid): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetNextIndex(tag, hashid, idx): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetPrevIndex(tag, hashid, idx): + raise DeprecatedIDCError, "Use python pickles instead." + +def SetHashLong(hashid, idx, value): + raise DeprecatedIDCError, "Use python pickles instead." + +def SetHashString(hashid, idx, value): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetHashLong(hashid, idx): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetHashString(hashid, idx): + raise DeprecatedIDCError, "Use python pickles instead." + +def DelHashElement(hashid, idx): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetFirstHashKey(hashid): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetNextHashKey(hashid, idx): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetLastHashKey(hashid): + raise DeprecatedIDCError, "Use python pickles instead." + +def GetPrevHashKey(hashid, idx): + raise DeprecatedIDCError, "Use python pickles instead." + + +#---------------------------------------------------------------------------- +# S O U R C E F I L E / L I N E N U M B E R S +#---------------------------------------------------------------------------- +def AddSourceFile(ea1, ea2, filename): + """ + Mark a range of address as belonging to a source file + An address range may belong only to one source file. + A source file may be represented by several address ranges. + + @param ea1: linear address of start of the address range + @param ea2: linear address of end of the address range + @param filename: name of source file. + + @return: 1-ok, 0-failed. + + @note: IDA can keep information about source files used to create the program. + Each source file is represented by a range of addresses. + A source file may contains several address ranges. + """ + return idaapi.add_sourcefile(ea1, ea2, filename) + + +def GetSourceFile(ea): + """ + Get name of source file occupying the given address + + @param ea: linear address + + @return: NULL - source file information is not found + otherwise returns pointer to file name + """ + return idaapi.get_sourcefile(ea) + + +def DelSourceFile(ea): + """ + Delete information about the source file + + @param ea: linear address belonging to the source file + + @return: NULL - source file information is not found + otherwise returns pointer to file name + """ + return idaapi.del_sourcefile(ea) + + +def SetLineNumber(ea, lnnum): + """ + Set source line number + + @param ea: linear address + @param lnnum: number of line in the source file + + @return: None + """ + idaapi.set_source_linnum(ea, lnnum) + + +def GetLineNumber(ea): + """ + Get source line number + + @param ea: linear address + + @return: number of line in the source file or -1 + """ + return idaapi.get_source_linnum(ea) + + +def DelLineNumber(ea): + """ + Delete information about source line number + + @param ea: linear address + + @return: None + """ + idaapi.del_source_linnum(ea) + + +#---------------------------------------------------------------------------- +# T Y P E L I B R A R I E S +#---------------------------------------------------------------------------- + +def LoadTil(name): + """ + Load a type library + + @param name: name of type library. + @return: 1-ok, 0-failed. + """ + til = idaapi.add_til2(name, idaapi.ADDTIL_DEFAULT) + + if til: + return 1 + else: + return 0 + + +def Til2Idb(idx, type_name): + """ + Copy information from type library to database + Copy structure, union, or enum definition from the type library + to the IDA database. + + @param idx: the position of the new type in the list of + types (structures or enums) -1 means at the end of the list + @param type_name: name of type to copy + + @return: BADNODE-failed, otherwise the type id (structure id or enum id) + """ + return idaapi.til2idb(idx, type_name) + + +def GetType(ea): + """ + Get type of function/variable + + @param ea: the address of the object + + @return: type string or None if failed + """ + return idaapi.idc_get_type(ea) + +def SizeOf(typestr): + """ + Returns the size of the type. It is equivalent to IDC's sizeof(). + Use name, tp, fld = idc.ParseType() ; Sizeof(fld) to retrieve the size + @return: -1 if typestring is not valid otherwise the size of the type + """ + return idaapi.get_type_size0(idaapi.cvar.idati, typestr) + +def GuessType(ea): + """ + Guess type of function/variable + + @param ea: the address of the object, can be the structure member id too + + @return: type string or None if failed + """ + return idaapi.idc_guess_type(ea) + + +def SetType(ea, newtype): + """ + Set type of function/variable + + @param ea: the address of the object + @param newtype: the type string in C declaration form. + Must contain the closing ';' + if specified as an empty string, then the + assciated with 'ea' will be deleted + + @return: 1-ok, 0-failed. + """ + return idaapi.apply_cdecl2(idaapi.cvar.idati, ea, newtype) + +def ParseType(inputtype, flags): + """ + Parse type declaration + + @param inputtype: file name or C declarations (depending on the flags) + @param flags: combination of PT_... constants or 0 + + @return: None on failure or (name, type, fields) tuple + """ + return idaapi.idc_parse_decl(idaapi.cvar.idati, inputtype, flags) + +def ParseTypes(inputtype, flags): + """ + 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 idaapi.idc_parse_types(inputtype, flags) + + +PT_FILE = 0x0001 # input if a file name (otherwise contains type declarations) +PT_SILENT = 0x0002 # silent mode +PT_PAKDEF = 0x0000 # default pack value +PT_PAK1 = 0x0010 # #pragma pack(1) +PT_PAK2 = 0x0020 # #pragma pack(2) +PT_PAK4 = 0x0030 # #pragma pack(4) +PT_PAK8 = 0x0040 # #pragma pack(8) +PT_PAK16 = 0x0050 # #pragma pack(16) + + +def GetMaxLocalType(): + """ + Get number of local types + 1 + + @return: value >= 1. 1 means that there are no local types. + """ + return idaapi.get_ordinal_qty(idaapi.cvar.idati) + + +def SetLocalType(ordinal, input, flags): + """ + Parse one type declaration and store it in the specified slot + + @param ordinal: slot number (1...NumberOfLocalTypes) + -1 means allocate new slot or reuse the slot + of the existing named type + @param input: C declaration. Empty input empties the slot + @param flags: combination of PT_... constants or 0 + + @return: slot number or 0 if error + """ + return idaapi.idc_set_local_type(ordinal, input, flags) + + +def GetLocalType(ordinal, flags): + """ + Retrieve a local type declaration + + @param ordinal: slot number (1...NumberOfLocalTypes) + @param flags: any of PRTYPE_* constants + + @return: local type as a C declaration or "" + + @note: This function can return types strings up to 64KiB. Use idaapi.idc_get_local_type() + for larger types. + """ + res,str = idaapi.idc_get_local_type(ordinal, flags, 2**16) + return str + +PRTYPE_1LINE = 0x0000 # print to one line +PRTYPE_MULTI = 0x0001 # print to many lines +PRTYPE_TYPE = 0x0002 # print type declaration (not variable declaration) +PRTYPE_PRAGMA = 0x0004 # print pragmas for alignment + + +def GetLocalTypeName(ordinal): + """ + Retrieve a local type name + + @param ordinal: slot number (1...NumberOfLocalTypes) + + returns: local type name or None + """ + return idaapi.idc_get_local_type_name(ordinal) + + +# ---------------------------------------------------------------------------- +# H I D D E N A R E A S +# ---------------------------------------------------------------------------- +def HideArea(start, end, description, header, footer, color): + """ + Hide an area + + Hidden areas - address ranges which can be replaced by their descriptions + + @param start: area start + @param end: area end + @param description: description to display if the area is collapsed + @param header: header lines to display if the area is expanded + @param footer: footer lines to display if the area is expanded + @param color: RGB color code (-1 means default color) + + @returns: !=0 - ok + """ + return idaapi.add_hidden_area(start, end, description, header, footer, color) + + +def SetHiddenArea(ea, visible): + """ + Set hidden area state + + @param ea: any address belonging to the hidden area + @param visible: new state of the area + + @return: != 0 - ok + """ + ha = idaapi.get_hidden_area(ea) + + if not ha: + return 0 + else: + ha.visible = visible + return idaapi.update_hidden_area(ha) + + +def DelHiddenArea(ea): + """ + Delete a hidden area + + @param ea: any address belonging to the hidden area + @returns: != 0 - ok + """ + return idaapi.del_hidden_area(ea) + + +#-------------------------------------------------------------------------- +# D E B U G G E R I N T E R F A C E +#-------------------------------------------------------------------------- +def LoadDebugger(dbgname, use_remote): + """ + Load the debugger + + @param dbgname: debugger module name Examples: win32, linux, mac. + @param use_remote: 0/1: use remote debugger or not + + @note: This function is needed only when running idc scripts from the command line. + In other cases IDA loads the debugger module automatically. + """ + return idaapi.load_debugger(dbgname, use_remote) + + +def StartDebugger(path, args, sdir): + """ + Launch the debugger + + @param path: path to the executable file. + @param args: command line arguments + @param sdir: initial directory for the process + + @return: -1-failed, 0-cancelled by the user, 1-ok + + @note: For all args: if empty, the default value from the database will be used + See the important note to the StepInto() function + """ + return idaapi.start_process(path, args, sdir) + + +def StopDebugger(): + """ + Stop the debugger + Kills the currently debugger process and returns to the disassembly mode + + @return: success + """ + return idaapi.exit_process() + + +def PauseProcess(): + """ + Suspend the running process + Tries to suspend the process. If successful, the PROCESS_SUSPEND + debug event will arrive (see GetDebuggerEvent) + + @return: success + + @note: To resume a suspended process use the GetDebuggerEvent function. + See the important note to the StepInto() function + """ + return idaapi.suspend_process() + + +def GetProcessQty(): + """ + Take a snapshot of running processes and return their number. + """ + return idaapi.get_process_qty() + + +def GetProcessPid(idx): + """ + Get the process ID of a running process + + @param idx: number of process, is in range 0..GetProcessQty()-1 + + @return: 0 if failure + """ + pinfo = idaapi.process_info_t() + pid = idaapi.get_process_info(idx, pinfo) + if pid != idaapi.NO_PROCESS: + return pinfo.pid + else: + return 0 + + +def GetProccessName(idx): + """ + Get the name of a running process + + @param idx: number of process, is in range 0..GetProcessQty()-1 + + @return: None if failure + """ + pinfo = idaapi.process_info_t() + pid = idaapi.get_process_info(idx, pinfo) + if pid != idaapi.NO_PROCESS: + return pinfo.name + else: + return "" + + +def AttachProcess(pid, event_id): + """ + Attach the debugger to a running process + + @param pid: PID of the process to attach to. If NO_PROCESS, a dialog box + will interactively ask the user for the process to attach to. + @param event_id: reserved, must be -1 + + @return: + - -2: impossible to find a compatible process + - -1: impossible to attach to the given process (process died, privilege + needed, not supported by the debugger plugin, ...) + - 0: the user cancelled the attaching to the process + - 1: the debugger properly attached to the process + @note: See the important note to the StepInto() function + """ + return idaapi.attach_process(pid, event_id) + + +def DetachProcess(): + """ + Detach the debugger from the debugged process. + + @return: success + """ + return idaapi.detach_process() + + +def GetThreadQty(): + """ + Get number of threads. + + @return: number of threads + """ + return idaapi.get_thread_qty() + + +def GetThreadId(idx): + """ + Get the ID of a thread + + @param idx: number of thread, is in range 0..GetThreadQty()-1 + + @return: -1 if failure + """ + return idaapi.getn_thread(idx) + + +def GetCurrentThreadId(): + """ + Get current thread ID + + @return: -1 if failure + """ + return idaapi.get_current_thread() + + +def SelectThread(tid): + """ + Select the given thread as the current debugged thread. + + @param tid: ID of the thread to select + + @return: success + + @note: The process must be suspended to select a new thread. + """ + return idaapi.select_thread(tid) + + +def SuspendThread(tid): + """ + Suspend thread + + @param tid: thread id + + @return: -1:network error, 0-failed, 1-ok + + @note: Suspending a thread may deadlock the whole application if the suspended + was owning some synchronization objects. + """ + return idaapi.suspend_thread(tid) + + +def ResumeThread(tid): + """ + Resume thread + + @param tid: thread id + + @return: -1:network error, 0-failed, 1-ok + """ + return idaapi.resume_thread(tid) + + +def _get_modules(): + """ + INTERNAL: Enumerate process modules + """ + module = idaapi.module_info_t() + result = idaapi.get_first_module(module) + while result: + yield module + result = idaapi.get_next_module(module) + + +def GetFirstModule(): + """ + Enumerate process modules + + @return: first module's base address or None on failure + """ + for module in _get_modules(): + return module.base + else: + return None + + +def GetNextModule(base): + """ + Enumerate process modules + + @param base: previous module's base address + + @return: next module's base address or None on failure + """ + foundit = False + for module in _get_modules(): + if foundit: + return module.base + if module.base == base: + foundit = True + else: + return None + + +def GetModuleName(base): + """ + Get process module name + + @param base: the base address of the module + + @return: required info or 0 + """ + for module in _get_modules(): + if module.base == base: + return module.name + else: + return 0 + + +def GetModuleSize(base): + """ + Get process module size + + @param base: the base address of the module + + @return: required info or -1 + """ + for module in _get_modules(): + if module.base == base: + return module.size + else: + return -1 + + +def StepInto(): + """ + Execute one instruction in the current thread. + Other threads are kept suspended. + + @return: success + + @note: You must call GetDebuggerEvent() after this call + in order to find out what happened. Normally you will + get the STEP event but other events are possible (for example, + an exception might occur or the process might exit). + This remark applies to all execution control functions. + The event codes depend on the issued command. + """ + return idaapi.step_into() + + +def StepOver(): + """ + Execute one instruction in the current thread, + but without entering into functions + Others threads keep suspended. + See the important note to the StepInto() function + + @return: success + """ + return idaapi.step_over() + + +def RunTo(ea): + """ + Execute the process until the given address is reached. + If no process is active, a new process is started. + See the important note to the StepInto() function + + @return: success + """ + return idaapi.run_to(ea) + + +def StepUntilRet(): + """ + Execute instructions in the current thread until + a function return instruction is reached. + Other threads are kept suspended. + See the important note to the StepInto() function + + @return: success + """ + return idaapi.step_until_ret() + + +def GetDebuggerEvent(wfne, timeout): + """ + Wait for the next event + This function (optionally) resumes the process + execution and wait for a debugger event until timeout + + @param wfne: combination of WFNE_... constants + @param timeout: number of seconds to wait, -1-infinity + + @return: debugger event codes, see below + """ + return idaapi.wait_for_next_event(wfne, timeout) + + +def ResumeProcess(): + return GetDebuggerEvent(WFNE_CONT|WFNE_NOWAIT, 0) + +def SendDbgCommand(cmd): + """Sends a command to the debugger module 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 + +# wfne flag is combination of the following: +WFNE_ANY = 0x0001 # return the first event (even if it doesn't suspend the process) + # if the process is still running, the database + # does not reflect the memory state. you might want + # to call RefreshDebuggerMemory() in this case +WFNE_SUSP = 0x0002 # wait until the process gets suspended +WFNE_SILENT = 0x0004 # 1: be slient, 0:display modal boxes if necessary +WFNE_CONT = 0x0008 # continue from the suspended state +WFNE_NOWAIT = 0x0010 # do not wait for any event, immediately return DEC_TIMEOUT + # (to be used with WFNE_CONT) + +# debugger event codes +NOTASK = -2 # process does not exist +DBG_ERROR = -1 # error (e.g. network problems) +DBG_TIMEOUT = 0 # timeout +PROCESS_START = 0x00000001 # New process started +PROCESS_EXIT = 0x00000002 # Process stopped +THREAD_START = 0x00000004 # New thread started +THREAD_EXIT = 0x00000008 # Thread stopped +BREAKPOINT = 0x00000010 # Breakpoint reached +STEP = 0x00000020 # One instruction executed +EXCEPTION = 0x00000040 # Exception +LIBRARY_LOAD = 0x00000080 # New library loaded +LIBRARY_UNLOAD = 0x00000100 # Library unloaded +INFORMATION = 0x00000200 # User-defined information +SYSCALL = 0x00000400 # Syscall (not used yet) +WINMESSAGE = 0x00000800 # Window message (not used yet) +PROCESS_ATTACH = 0x00001000 # Attached to running process +PROCESS_DETACH = 0x00002000 # Detached from process +PROCESS_SUSPEND = 0x00004000 # Process has been suspended + + +def RefreshDebuggerMemory(): + """ + Refresh debugger memory + Upon this call IDA will forget all cached information + about the debugged process. This includes the segmentation + information and memory contents (register cache is managed + automatically). Also, this function refreshes exported name + from loaded DLLs. + You must call this function before using the segmentation + information, memory contents, or names of a non-suspended process. + This is an expensive call. + """ + return idaapi.refresh_debugger_memory() + + +def TakeMemorySnapshot(only_loader_segs): + """ + Take memory snapshot of the debugged process + + @param only_loader_segs: 0-copy all segments to idb + 1-copy only SFL_LOADER segments + """ + return idaapi.take_memory_snapshot(only_loader_segs) + + +def GetProcessState(): + """ + Get debugged process state + + @return: one of the DBG_... constants (see below) + """ + return idaapi.get_process_state() + +DSTATE_SUSP_FOR_EVENT = -2 # process is currently suspended to react to a debug event +DSTATE_SUSP = -1 # process is suspended +DSTATE_NOTASK = 0 # no process is currently debugged +DSTATE_RUN = 1 # process is running +DSTATE_RUN_WAIT_ATTACH = 2 # process is running, waiting for process properly attached +DSTATE_RUN_WAIT_END = 3 # process is running, but the user asked to kill/detach the process + # remark: in this case, most events are ignored + +""" + Get various information about the current debug event + These functions are valid only when the current event exists + (the process is in the suspended state) +""" + +# For all events: + +def GetEventId(): + """ + Get ID of debug event + + @return: event ID + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return ev.eid + + +def GetEventPid(): + """ + Get process ID for debug event + + @return: process ID + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return ev.pid + + +def GetEventTid(): + """ + Get type ID for debug event + + @return: type ID + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return ev.tid + + +def GetEventEa(): + """ + Get ea for debug event + + @return: ea + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return ev.ea + + +def IsEventHandled(): + """ + Is the debug event handled? + + @return: boolean + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return ev.handled + + +# For PROCESS_START, PROCESS_ATTACH, LIBRARY_LOAD events: + +def GetEventModuleName(): + """ + Get module name for debug event + + @return: module name + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return idaapi.get_event_module_name(ev) + + +def GetEventModuleBase(): + """ + Get module base for debug event + + @return: module base + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return idaapi.get_event_module_base(ev) + + +def GetEventModuleSize(): + """ + Get module size for debug event + + @return: module size + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return idaapi.get_event_module_size(ev) + + +def GetEventExitCode(): + """ + Get exit code for debug event + + @return: exit code for PROCESS_EXIT, THREAD_EXIT events + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return ev.exit_code + + +def GetEventInfo(): + """ + Get debug event info + + @return: event info: for LIBRARY_UNLOAD (unloaded library name) + for INFORMATION (message to display) + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return idaapi.get_event_info(ev) + + +def GetEventBptHardwareEa(): + """ + Get hardware address for BREAKPOINT event + + @return: hardware address + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return idaapi.get_event_bpt_hea(ev) + + +def GetEventExceptionCode(): + """ + Get exception code for EXCEPTION event + + @return: exception code + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return idaapi.get_event_exc_code(ev) + + +def GetEventExceptionEa(): + """ + Get address for EXCEPTION event + + @return: adress of exception + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return idaapi.get_event_exc_ea(ev) + + +def CanExceptionContinue(): + """ + Can it continue after EXCEPTION event? + + @return: boolean + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return idaapi.can_exc_continue(ev) + + +def GetEventExceptionInfo(): + """ + Get info for EXCEPTION event + + @return: info string + """ + ev = idaapi.get_debug_event() + assert ev, "Could not retrieve debug event" + return idaapi.get_event_exc_info(ev) + + +def SetDebuggerOptions(opt): + """ + Get/set debugger options + + @param opt: combination of DOPT_... constants + + @return: old options + """ + return idaapi.set_debugger_options(opt) + + +DOPT_SEGM_MSGS = 0x00000001 # print messages on debugger segments modifications +DOPT_START_BPT = 0x00000002 # break on process start +DOPT_THREAD_MSGS = 0x00000004 # print messages on thread start/exit +DOPT_THREAD_BPT = 0x00000008 # break on thread start/exit +DOPT_BPT_MSGS = 0x00000010 # print message on breakpoint +DOPT_LIB_MSGS = 0x00000040 # print message on library load/unlad +DOPT_LIB_BPT = 0x00000080 # break on library load/unlad +DOPT_INFO_MSGS = 0x00000100 # print message on debugging information +DOPT_INFO_BPT = 0x00000200 # break on debugging information +DOPT_REAL_MEMORY = 0x00000400 # don't hide breakpoint instructions +DOPT_REDO_STACK = 0x00000800 # reconstruct the stack +DOPT_ENTRY_BPT = 0x00001000 # break on program entry point +DOPT_EXCDLG = 0x00006000 # exception dialogs: + +EXCDLG_NEVER = 0x00000000 # never display exception dialogs +EXCDLG_UNKNOWN = 0x00002000 # display for unknown exceptions +EXCDLG_ALWAYS = 0x00006000 # always display + +DOPT_LOAD_DINFO = 0x00008000 # automatically load debug files (pdb) + + +def SetRemoteDebugger(hostname, password, portnum): + """ + Set remote debugging options + + @param hostname: remote host name or address if empty, revert to local debugger + @param password: password for the debugger server + @param portnum: port number to connect (-1: don't change) + + @return: nothing + """ + return idaapi.set_remote_debugger(hostname, password, portnum) + + +def GetExceptionQty(): + """ + Get number of defined exception codes + """ + return idaapi.get_exception_qty() + + +def GetExceptionCode(idx): + """ + Get exception code + + @param idx: number of exception in the vector (0..GetExceptionQty()-1) + + @return: exception code (0 - error) + """ + return idaapi.get_exception_code(idx) + + +def GetExceptionName(code): + """ + Get exception information + + @param code: exception code + + @return: "" on error + """ + return idaapi.get_exception_name(code) + + +def GetExceptionFlags(code): + """ + Get exception information + + @param code: exception code + + @return: -1 on error + """ + return idaapi.get_exception_flags(code) + +def DefineException(code, name, desc, flags): + """ + Add exception handling information + + @param code: exception code + @param name: exception name + @param desc: exception description + @param flags: exception flags (combination of EXC_...) + + @return: failure description or "" + """ + return idaapi.define_exception(code, name, desc, flags) + +EXC_BREAK = 0x0001 # break on the exception +EXC_HANDLE = 0x0002 # should be handled by the debugger? + + +def SetExceptionFlags(code, flags): + """ + Set exception flags + + @param code: exception code + @param flags: exception flags (combination of EXC_...) + """ + return idaapi.set_exception_flags(code, flags) + + +def ForgetException(code): + """ + Delete exception handling information + + @param code: exception code + """ + return idaapi.forget_exception(code) + + +def GetRegValue(name): + """ + Get register value + + @param name: the register name + + @note: The debugger should be running. otherwise the function fails + the register name should be valid. + It is not necessary to use this function to get register values + because a register name in the script will do too. + + @return: register value (integer or floating point) + """ + rv = idaapi.regval_t() + res = idaapi.get_reg_val(name, rv) + assert res, "get_reg_val() failed, bogus name perhaps?" + return rv.ival + + +def SetRegValue(value, name): + """ + Set register value + + @param name: the register name + @param value: new register value + + @note: The debugger should be running + It is not necessary to use this function to set register values. + A register name in the left side of an assignment will do too. + """ + rv = idaapi.regval_t() + if type(value)==types.StringType: + value = int(value) + elif type(value)!=types.IntType: + print "SetRegValue: value must be integer!" + return BADADDR + + if value<0: + #ival_set cannot handle negative numbers + value &= 0xFFFFFFFF + + rv.ival = value + return idaapi.set_reg_val(name, rv) + + +def GetBptQty(): + """ + Get number of breakpoints. + + @return: number of breakpoints + """ + return idaapi.get_bpt_qty() + + +def GetBptEA(n): + """ + Get breakpoint address + + @param n: number of breakpoint, is in range 0..GetBptQty()-1 + + @return: addresss of the breakpoint or BADADDR + """ + bpt = idaapi.bpt_t() + + if idaapi.getn_bpt(n, bpt): + return bpt.ea + else: + return BADADDR + + +def GetBptAttr(ea, bptattr): + """ + Get the characteristics of a breakpoint + + @param ea: any address in the breakpoint range + @param bptattr: the desired attribute code, one of BPTATTR_... constants + + @return: the desired attribute value or -1 + """ + bpt = idaapi.bpt_t() + + if not idaapi.get_bpt(ea, bpt): + return -1 + else: + if bptattr == BPTATTR_EA: + return bpt.ea + if bptattr == BPTATTR_SIZE: + return bpt.size + if bptattr == BPTATTR_TYPE: + return bpt.type + if bptattr == BPTATTR_COUNT: + return bpt.pass_count + if bptattr == BPTATTR_FLAGS: + return bpt.flags + if bptattr == BPTATTR_COND: + return bpt.condition + return -1 + + +BPTATTR_EA = 1 # starting address of the breakpoint +BPTATTR_SIZE = 2 # size of the breakpoint (undefined for software breakpoint) + +# type of the breakpoint +BPTATTR_TYPE = 3 + +# Breakpoint types: +BPT_EXEC = 0 # Hardware: Execute instruction +BPT_WRITE = 1 # Hardware: Write access +BPT_RDWR = 3 # Hardware: Read/write access +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 + +BPTATTR_COND = 6 # Breakpoint condition. NOTE: the return value is a string in this case + + +def SetBptAttr(address, bptattr, value): + """ + modifiable characteristics of a breakpoint + + @param address: any address in the breakpoint range + @param bptattr: the attribute code, one of BPTATTR_* constants + BPTATTR_CND is not allowed, see SetBptCnd() + @param value: the attibute value + + @return: success + """ + bpt = idaapi.bpt_t() + + if not idaapi.get_bpt(address, bpt): + return False + else: + if bptattr not in [ BPTATTR_SIZE, BPTATTR_TYPE, BPTATTR_FLAGS, BPTATTR_COUNT ]: + return False + if bptattr == BPTATTR_SIZE: + bpt.size = value + if bptattr == BPTATTR_TYPE: + bpt.type = value + if bptattr == BPTATTR_COUNT: + bpt.pass_count = value + if bptattr == BPTATTR_FLAGS: + bpt.flags = value + + idaapi.update_bpt(bpt) + return True + + +def SetBptCnd(ea, cnd): + """ + Set breakpoint condition + + @param ea: any address in the breakpoint range + @param cnd: breakpoint condition + + @return: success + """ + bpt = idaapi.bpt_t() + + if not idaapi.get_bpt(ea, bpt): + return False + + bpt.condition = cnd + + return idaapi.update_bpt(bpt) + + +def AddBptEx(ea, size, bpttype): + """ + Add a new breakpoint + + @param ea: any address in the process memory space: + @param size: size of the breakpoint (irrelevant for software breakpoints): + @param bpttype: type of the breakpoint (one of BPT_... constants) + + @return: success + + @note: Only one breakpoint can exist at a given address. + """ + return idaapi.add_bpt(ea, size, bpttype) + + +def AddBpt(ea): return AddBptEx(ea, 0, BPT_SOFT) + + +def DelBpt(ea): + """ + Delete breakpoint + + @param ea: any address in the process memory space: + + @return: success + """ + return idaapi.del_bpt(ea) + + +def EnableBpt(ea, enable): + """ + Enable/disable breakpoint + + @param ea: any address in the process memory space + + @return: success + + @note: Disabled breakpoints are not written to the process memory + """ + return idaapi.enable_bpt(ea, enable) + + +def CheckBpt(ea): + """ + Check a breakpoint + + @param ea: address in the process memory space + + @return: one of BPTCK_... constants + """ + return idaapi.check_bpt(ea) + +BPTCK_NONE = -1 # breakpoint does not exist +BPTCK_NO = 0 # breakpoint is disabled +BPTCK_YES = 1 # breakpoint is enabled +BPTCK_ACT = 2 # breakpoint is active (written to the process) + + +def EnableTracing(trace_level, enable): + """ + Enable step tracing + + @param trace_level: what kind of trace to modify + @param enable: 0: turn off, 1: turn on + + @return: success + """ + assert trace_level in [ TRACE_STEP, TRACE_INSN, TRACE_FUNC ], \ + "trace_level must be one of TRACE_* constants" + + if trace_level == TRACE_STEP: + return idaapi.enable_step_trace(enable) + + if trace_level == TRACE_INSN: + return idaapi.enable_insn_trace(enable) + + if trace_level == TRACE_FUNC: + return idaapi.enable_func_trace(enable) + + return False + +TRACE_STEP = 0x0 # lowest level trace. trace buffers are not maintained +TRACE_INSN = 0x1 # instruction level trace +TRACE_FUNC = 0x2 # function level trace (calls & rets) + +#-------------------------------------------------------------------------- +# C O L O R S +#-------------------------------------------------------------------------- + +def GetColor(ea, what): + """ + Get item color + + @param ea: address of the item + @param what: type of the item (one of CIC_* constants) + + @return: color code in RGB (hex 0xBBGGRR) + """ + if what not in [ CIC_ITEM, CIC_FUNC, CIC_SEGM ]: + raise ValueError, "'what' must be one of CIC_ITEM, CIC_FUNC and CIC_SEGM" + + if what == CIC_ITEM: + return idaapi.get_item_color(ea) + + if what == CIC_FUNC: + func = idaapi.get_func(ea) + if func: + return func.color + else: + return DEFCOLOR + + if what == CIC_SEGM: + seg = idaapi.getseg(ea) + if seg: + return seg.color + else: + return DEFCOLOR + +# color item codes: +CIC_ITEM = 1 # one instruction or data +CIC_FUNC = 2 # function +CIC_SEGM = 3 # segment + +DEFCOLOR = 0xFFFFFFFF # Default color + + +def SetColor(ea, what, color): + """ + Set item color + + @param ea: address of the item + @param what: type of the item (one of CIC_* constants) + @param color: new color code in RGB (hex 0xBBGGRR) + + @return: success (True or False) + """ + if what not in [ CIC_ITEM, CIC_FUNC, CIC_SEGM ]: + raise ValueError, "'what' must be one of CIC_ITEM, CIC_FUNC and CIC_SEGM" + + if what == CIC_ITEM: + return idaapi.set_item_color(ea, color) + + if what == CIC_FUNC: + func = idaapi.get_func(ea) + if func: + func.color = color + return bool(idaapi.update_func(func)) + else: + return False + + if what == CIC_SEGM: + seg = idaapi.getseg(ea) + if seg: + seg.color = color + return bool(seg.update()) + else: + return False + + +#-------------------------------------------------------------------------- +# X M L +#-------------------------------------------------------------------------- + +def SetXML(path, name, value): + """ + Set or update one or more XML values. + + @param path: XPath expression of elements where to create value(s) + @param name: name of the element/attribute + (use @XXX for an attribute) to create. + If 'name' is empty, the elements or + attributes returned by XPath are directly + updated to contain the new 'value'. + @param value: value of the element/attribute + + @return: success (True or False) + """ + return idaapi.set_xml(path, name, value) + + +def GetXML(path): + """ + Get one XML value. + + @param path: XPath expression to an element + or attribute whose value is requested + + @return: the value, None if failed + """ + v = idaapi.value_t() + if idaapi.get_xml(path): + return v.str + else: + return None + + +#---------------------------------------------------------------------------- +# A R M S P E C I F I C +#---------------------------------------------------------------------------- +def ArmForceBLJump(ea): + """ + Some ARM compilers in Thumb mode use BL (branch-and-link) + instead of B (branch) for long jumps, since BL has more range. + By default, IDA tries to determine if BL is a jump or a call. + You can override IDA's decision using commands in Edit/Other menu + (Force BL call/Force BL jump) or the following two functions. + + Force BL instruction to be a jump + + @param ea: address of the BL instruction + + @return: 1-ok, 0-failed + """ + return Eval("ArmForceBLJump(0x%x)"%ea) + + +def ArmForceBLCall(ea): + """ + Force BL instruction to be a call + + @param ea: address of the BL instruction + + @return: 1-ok, 0-failed + """ + return Eval("ArmForceBLCall(0x%x)"%ea) + + +#-------------------------------------------------------------------------- +# Compatibility macros: +def Compile(file): return CompileEx(file, 1) +def OpOffset(ea,base): return OpOff(ea,-1,base) +def OpNum(ea): return OpNumber(ea,-1) +def OpChar(ea): return OpChr(ea,-1) +def OpSegment(ea): return OpSeg(ea,-1) +def OpDec(ea): return OpDecimal(ea,-1) +def OpAlt1(ea, opstr): return OpAlt(ea, 0, opstr) +def OpAlt2(ea, opstr): return OpAlt(ea, 1, opstr) +def StringStp(x): return SetCharPrm(INF_ASCII_BREAK,x) +def LowVoids(x): return SetLongPrm(INF_LOW_OFF,x) +def HighVoids(x): return SetLongPrm(INF_HIGH_OFF,x) +def TailDepth(x): return SetLongPrm(INF_MAXREF,x) +def Analysis(x): return SetCharPrm(INF_AUTO,x) +def Tabs(x): return SetCharPrm(INF_ENTAB,x) +#def Comments(x): SetCharPrm(INF_CMTFLAG,((x) ? (SW_ALLCMT|GetCharPrm(INF_CMTFLAG)) : (~SW_ALLCMT&GetCharPrm(INF_CMTFLAG)))) +def Voids(x): return SetCharPrm(INF_VOIDS,x) +def XrefShow(x): return SetCharPrm(INF_XREFNUM,x) +def Indent(x): return SetCharPrm(INF_INDENT,x) +def CmtIndent(x): return SetCharPrm(INF_COMMENT,x) +def AutoShow(x): return SetCharPrm(INF_SHOWAUTO,x) +def MinEA(): return GetLongPrm(INF_MIN_EA) +def MaxEA(): return GetLongPrm(INF_MAX_EA) +def BeginEA(): return GetLongPrm(INF_BEGIN_EA) +def set_start_cs(x): return SetLongPrm(INF_START_CS,x) +def set_start_ip(x): return SetLongPrm(INF_START_IP,x) + +def WriteMap(filepath): + return GenerateFile(OFILE_MAP, filepath, 0, BADADDR, GENFLG_MAPSEG|GENFLG_MAPNAME) + +def WriteTxt(filepath, ea1, ea2): + return GenerateFile(OFILE_ASM, filepath, ea1, ea2, 0) + +def WriteExe(filepath): + return GenerateFile(OFILE_EXE, filepath, 0, BADADDR, 0) + +def AddConst(enum_id,name,value): return AddConstEx(enum_id,name,value,-1) +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) +#def GetFrameRegsSize(ea): return GetFunctionAttr(ea, FUNCATTR_FRREGS) +#def GetFrameArgsSize(ea): return GetFunctionAttr(ea, FUNCATTR_ARGSIZE) +#def GetFunctionFlags(ea): return GetFunctionAttr(ea, FUNCATTR_FLAGS) +#def SetFunctionFlags(ea, flags): return SetFunctionAttr(ea, FUNCATTR_FLAGS, flags) + +#def SegStart(ea): return GetSegmentAttr(ea, SEGATTR_START) +#def SegEnd(ea): return GetSegmentAttr(ea, SEGATTR_END) +#def SetSegmentType(ea, type): return SetSegmentAttr(ea, SEGATTR_TYPE, type) + +def SegCreate(a1, a2, base, use32, align, comb): return AddSeg(a1, a2, base, use32, align, comb) +def SegDelete(ea, flags): return DelSeg(ea, flags) +def SegBounds(ea, startea, endea, flags): return SetSegBounds(ea, startea, endea, flags) +def SegRename(ea, name): return RenameSeg(ea, name) +def SegClass(ea, segclass): return SetSegClass(ea, segclass) +def SegAddrng(ea, bitness): return SetSegAddressing(ea, bitness) +def SegDefReg(ea, reg, value): return SetSegDefReg(ea, reg, value) + + +def Comment(ea): return GetCommentEx(ea, 0) +def RptCmt(ea): return GetCommentEx(ea, 1) + +def SetReg(ea, reg, value): return SetRegEx(ea, reg, value, SR_user) + + +# Convenience functions: +def here(): return ScreenEA() +def isEnabled(ea): return (PrevAddr(ea+1)==ea) + +# Obsolete segdel macros: +SEGDEL_PERM = 0x0001 # permanently, i.e. disable addresses +SEGDEL_KEEP = 0x0002 # keep information (code & data, etc) +SEGDEL_SILENT = 0x0004 # be silent + +ARGV = [] +"""The command line arguments passed to IDA via the -S switch.""" + +# END OF IDC COMPATIBILY CODE diff --git a/python/init.py b/python/init.py index d648c32..1b7ad65 100644 --- a/python/init.py +++ b/python/init.py @@ -1,228 +1,79 @@ -#!/usr/bin/env python -# ----------------------------------------------------------------------- -# 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. -# ----------------------------------------------------------------------- -# init.py - Essential init routines -# ----------------------------------------------------------------------- -import os -import sys -import time -import warnings - -import _idaapi - -# __EA64__ is set if IDA is running in 64-bit mode -__EA64__ = _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL - -# ----------------------------------------------------------------------- -def addscriptpath(script): - """ - Add the path part of the scriptfile to the system path to - allow modules to be loaded from the same place. - - Each path is added only once. - """ - pathfound = 0 - - scriptpath = os.path.dirname(script) - - for pathitem in sys.path: - if pathitem == scriptpath: - pathfound = 1 - break - - if pathfound == 0: - sys.path.append(scriptpath) - - # Add the script to ScriptBox if it's not there yet - if not script in scriptbox.list: - scriptbox.list.insert(0, script) - -# ------------------------------------------------------------ -def runscript(script): - """ - Run the specified script after adding its directory path to - system path. - - This function is used by the low-level plugin code. - """ - addscriptpath(script) - watchdog.reset() - # Save the argv, path, I/O and base modules for later cleanup - argv = sys.argv - path = sys.path - stdio = [sys.stdin, sys.stdout, sys.stderr] - basemodules = sys.modules.copy() - sys.argv = [ script ] - # Adjust the __file__ path in the globals we pass to the script - g = globals() - old__file__ = g['__file__'] if '__file__' in g else '' - g['__file__'] = script - try: - execfile(script, g) - except: - raise - finally: - # Restore the globals to the state before the script was run - g['__file__'] = old__file__ - sys.argv = argv - sys.path = path - sys.stdin, sys.stdout, sys.stderr = stdio - # Clean up the modules loaded by the script - for module in sys.modules.keys(): - if not module in basemodules: - del(sys.modules[module]) - -# ----------------------------------------------------------------------- -def print_banner(): - banner = [ - "Python interpreter version %d.%d.%d %s (serial %d)" % sys.version_info, - "Copyright (c) 1990-2010 Python Software Foundation - http://www.python.org/", - "", - "IDAPython" + (" 64-bit" if __EA64__ else "") + " version %d.%d.%d %s (serial %d)" % IDAPYTHON_VERSION, - "Copyright (c) 2004-2010 Gergely Erdelyi - http://code.google.com/p/idapython/" - ] - sepline = '-' * max([len(s) for s in banner]) - - print sepline - print "\n".join(banner) - print sepline - -# ----------------------------------------------------------------------- -# Take over the standard text outputs -# ----------------------------------------------------------------------- -class MyStdOut: - """ - Dummy file-like class that receives stout and stderr - """ - def write(self, text): - # Swap out the unprintable characters - text = text.decode('ascii', 'replace').encode('ascii', 'replace') - # Print to IDA message window - _idaapi.msg(text.replace("%", "%%")) - - def flush(self): - pass - - def isatty(self): - return False - -# Redirect stderr and stdout to the IDA message window -sys.stdout = sys.stderr = MyStdOut() - -# Assign a default sys.argv -sys.argv = [""] - -# Have to make sure Python finds our modules -sys.path.append(_idaapi.idadir("python")) - -# ----------------------------------------------------------------------- -# Import all the required modules -# ----------------------------------------------------------------------- -from idaapi import Choose, get_user_idadir, cvar, Choose2, Appcall -from idc import * -from idautils import * -import idaapi - -# ----------------------------------------------------------------------- -# Build up the ScriptBox tool -# ----------------------------------------------------------------------- -class ScriptBox(Choose): - def __init__(self, list=None): - if list: - self.list = list - else: - self.list = [] - Choose.__init__(self, self.list, "ScriptBox", 1) - self.width = 50 - - def run(self): - if len(self.list) == 0: - Warning("ScriptBox history is empty.\nRun some script with Alt-9 and try again.") - return None - - n = self.choose() - - if n > 0: - runscript(self.list[n-1]) - - def addscript(self, scriptpath): - self.list.append(scriptpath) - -scriptbox = ScriptBox() - -# ----------------------------------------------------------------------- -# Watchdog to catch runaway scripts after a specified timeout -# -# Usage: -# watchdog.install() -# watchdog.activate(10) # Use 10-second timeout -# -# Note: The watchdog only works for code running inside -# functions, not in global/module namespace. -# ----------------------------------------------------------------------- -class WatchDog(): - """ - Python tracer-based watchdog class - """ - def __init__(self, timeout=10): - self.timestamp = 0 - self.timeout = timeout - self.installed = False - self.active = False - - def install(self): - """ Install the tracer function, required for the watchdog """ - if not self.installed: - sys.settrace(self.tracer) - self.installed = True - - def activate(self, timeout=None): - """ Activate the watchdog, with optional timeout change """ - assert self.installed, "WatchDog must be installed before activating" - if timeout: - self.timeout = timeout - self.reset() - self.active = True - - def deactivate(self): - """ Deactivate the watchdog """ - self.active = True - - def reset(self): - """ Reset the timer, useful for long-running scripts """ - self.timestamp = time.clock() - - def tracer(self, frame, event, arg): - """ Tracer function that receives the tracing events """ - if not self.active: - return None - if event == 'line': - if time.clock() - self.timestamp > self.timeout: - if AskYN(0, "The script has not finished in %d seconds\nWould you like to stop it now?" % self.timeout) == 1: - raise KeyboardInterrupt - else: - self.timestamp = time.clock() - return self.tracer - -watchdog = WatchDog(10) - -# ----------------------------------------------------------------------- -# Load the users personal init file -userrc = get_user_idadir() + os.sep + "idapythonrc.py" - -# ----------------------------------------------------------------------- -if os.path.exists(userrc): - runscript(userrc) - - # Remove the user script from the history - del scriptbox.list[0] - -# All done, ready to rock. +#!/usr/bin/env python +# ----------------------------------------------------------------------- +# 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. +# ----------------------------------------------------------------------- +# init.py - Essential init routines +# ----------------------------------------------------------------------- +import os +import sys +import time +import warnings +import _idaapi + +# __EA64__ is set if IDA is running in 64-bit mode +__EA64__ = _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL + +# ----------------------------------------------------------------------- +# Take over the standard text outputs +# ----------------------------------------------------------------------- +class IDAPythonStdOut: + """ + Dummy file-like class that receives stout and stderr + """ + def write(self, text): + # Swap out the unprintable characters + text = text.decode('ascii', 'replace').encode('ascii', 'replace') + # Print to IDA message window + _idaapi.msg(text.replace("%", "%%")) + + def flush(self): + pass + + def isatty(self): + return False + +# ----------------------------------------------------------------------- +def print_banner(): + banner = [ + "Python interpreter version %d.%d.%d %s (serial %d)" % sys.version_info, + "Copyright (c) 1990-2010 Python Software Foundation - http://www.python.org/", + "", + "IDAPython" + (" 64-bit" if __EA64__ else "") + " version %d.%d.%d %s (serial %d)" % IDAPYTHON_VERSION, + "Copyright (c) 2004-2010 Gergely Erdelyi - http://code.google.com/p/idapython/" + ] + sepline = '-' * max([len(s) for s in banner]) + + print sepline + print "\n".join(banner) + print sepline + +# ----------------------------------------------------------------------- + +# Redirect stderr and stdout to the IDA message window +sys.stdout = sys.stderr = IDAPythonStdOut() + +# Assign a default sys.argv +sys.argv = [""] + +# Have to make sure Python finds our modules +sys.path.append(_idaapi.idadir("python")) + +# Import all the required modules +from idaapi import Choose, get_user_idadir, cvar, Choose2, Appcall +from idc import * +from idautils import * +import idaapi + +# Load the users personal init file +userrc = get_user_idadir() + os.sep + "idapythonrc.py" +if os.path.exists(userrc): + idaapi.IDAPython_ExecScript(userrc, globals()) + +# All done, ready to rock. \ No newline at end of file diff --git a/pywraps.hpp b/pywraps.hpp index 1bb5b43..43b8936 100644 --- a/pywraps.hpp +++ b/pywraps.hpp @@ -7,17 +7,25 @@ #define PYUL_DEFINED #ifdef __EA64__ typedef unsigned PY_LONG_LONG pyul_t; + typedef PY_LONG_LONG pyl_t; #else typedef unsigned long pyul_t; + typedef long pyl_t; #endif #endif #ifdef __EA64__ - #define PY_FMT64 "K" + #define PY_FMT64 "K" + #define PY_SFMT64 "L" #else - #define PY_FMT64 "k" + #define PY_FMT64 "k" + #define PY_SFMT64 "l" #endif +#define S_IDAAPI_MODNAME "idaapi" +#define S_IDC_MODNAME "idc" +#define S_IDAAPI_EXECSCRIPT "IDAPython_ExecScript" + // Vector of PyObject* typedef qvector ppyobject_vec_t; @@ -27,6 +35,19 @@ typedef qvector ppyobject_vec_t; #define PY_ICID_BYREF 1 #define PY_ICID_OPAQUE 2 +//------------------------------------------------------------------------ +// Constants used with the notify_when() +#define NW_OPENIDB 0x0001 +#define NW_OPENIDB_SLOT 0 +#define NW_CLOSEIDB 0x0002 +#define NW_CLOSEIDB_SLOT 1 +#define NW_INITIDA 0x0004 +#define NW_INITIDA_SLOT 2 +#define NW_TERMIDA 0x0008 +#define NW_TERMIDA_SLOT 3 +#define NW_REMOVE 0x0010 // Uninstall flag +#define NW_EVENTSCNT 4 // Count of notify_when codes + //------------------------------------------------------------------------ // Constants used by the pyvar_to_idcvar and idcvar_to_pyvar functions #define CIP_FAILED -1 // Conversion error @@ -36,47 +57,72 @@ typedef qvector ppyobject_vec_t; //------------------------------------------------------------------------ // All the exported functions from PyWraps are forward declared here +insn_t *insn_t_get_clink(PyObject *self); +op_t *op_t_get_clink(PyObject *self); // Tries to import a module and swallows the exception if it fails and returns NULL -PyObject *PyImport_TryImportModule(const char *name); - -// Tries to get an attribute and swallows the exception if it fails and returns NULL -PyObject *PyObject_TryGetAttrString(PyObject *py_var, const char *attr); - -// Converts a Python number (LONGLONG or normal integer) to an IDC variable (VT_LONG or VT_INT64) -bool PyGetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var); - -// Converts a Python number to an uint64 and indicates whether the number was a long number -bool PyGetNumber(PyObject *py_var, uint64 *num, bool *is_64 = NULL); - -// Checks if an Python object can be treated like a sequence -bool PyIsSequenceType(PyObject *obj); - -// Returns an error string from the last exception (and clears it) -bool PyGetError(qstring *out = NULL); - +PyObject *PyImport_TryImportModule(const char *name); + +// Tries to get an attribute and swallows the exception if it fails and returns NULL +PyObject *PyObject_TryGetAttrString(PyObject *py_var, const char *attr); + +// Converts a Python number (LONGLONG or normal integer) to an IDC variable (VT_LONG or VT_INT64) +bool PyGetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var); + +// Converts a Python number to an uint64 and indicates whether the number was a long number +bool PyGetNumber(PyObject *py_var, uint64 *num, bool *is_64 = NULL); + +// Checks if an Python object can be treated like a sequence +bool PyIsSequenceType(PyObject *obj); + +// Returns an error string from the last exception (and clears it) +bool PyGetError(qstring *out = NULL); + // If an error occured (it calls PyGetError) it displays it and return TRUE bool PyShowErr(const char *cb_name); - -// [De]Initializes PyWraps -bool init_pywraps(); -void deinit_pywraps(); - -// Returns the string representation of a PyObject -bool PyObjectToString(PyObject *obj, qstring *out); - -// Converts Python variable to IDC variable -// gvar_sn is used in case the Python object was a created from a call to idcvar_to_pyvar and the IDC object was a VT_REF -int pyvar_to_idcvar( - PyObject *py_var, - idc_value_t *idc_var, - int *gvar_sn = NULL); - -// Converts from IDC to Python -// We support converting VT_REF IDC variable types -int idcvar_to_pyvar( - const idc_value_t &idc_var, + +// Utility function to create a class instance whose constructor takes zero arguments +PyObject *create_idaapi_class_instance0(const char *clsname); + +// Utility function to create linked class instances +PyObject *create_idaapi_linked_class_instance(const char *clsname, void *lnk); + +// [De]Initializes PyWraps +bool init_pywraps(); +void deinit_pywraps(); + +// Returns the string representation of a PyObject +bool PyObjectToString(PyObject *obj, qstring *out); + +// Utility function to convert a python object to an IDC object +// and sets a python exception on failure. +bool convert_pyobj_to_idc_exc(PyObject *py_obj, idc_value_t *idc_obj); + +// Converts Python variable to IDC variable +// gvar_sn is used in case the Python object was a created from a call to idcvar_to_pyvar and the IDC object was a VT_REF +int pyvar_to_idcvar( + PyObject *py_var, + idc_value_t *idc_var, + int *gvar_sn = NULL); + +// Converts from IDC to Python +// We support converting VT_REF IDC variable types +int idcvar_to_pyvar( + const idc_value_t &idc_var, PyObject **py_var); +// Walks a Python list or Sequence and calls the callback +Py_ssize_t pyvar_walk_list( + PyObject *py_list, + int (idaapi *cb)(PyObject *py_item, Py_ssize_t index, void *ud) = NULL, + void *ud = NULL); + +// Returns a reference to a class +PyObject *get_idaapi_attr(const char *attr); + +// notify_when() +bool pywraps_nw_term(); +bool pywraps_nw_notify(int slot, ...); +bool pywraps_nw_init(); #endif \ No newline at end of file diff --git a/swig/bytes.i b/swig/bytes.i index 2ebe7fc..a703cc4 100644 --- a/swig/bytes.i +++ b/swig/bytes.i @@ -61,9 +61,17 @@ %ignore term_flags; %ignore reset_flags; %ignore flush_flags; - -// TODO: These could be fixed if someone needs them. +%ignore data_type_t; +%ignore data_format_t; +%ignore get_custom_data_type; +%ignore get_custom_data_format; +%ignore unregister_custom_data_format; +%ignore register_custom_data_format; +%ignore unregister_custom_data_type; +%ignore register_custom_data_type; %ignore get_many_bytes; + +// TODO: This could be fixed (if needed) %ignore set_dbgmem_source; %include "bytes.hpp" @@ -76,6 +84,12 @@ %rename (nextthat) py_nextthat; %rename (prevthat) py_prevthat; +%rename (get_custom_data_type) py_get_custom_data_type; +%rename (get_custom_data_format) py_get_custom_data_format; +%rename (unregister_custom_data_format) py_unregister_custom_data_format; +%rename (register_custom_data_format) py_register_custom_data_format; +%rename (unregister_custom_data_type) py_unregister_custom_data_type; +%rename (register_custom_data_type) py_register_custom_data_type; %rename (get_many_bytes) py_get_many_bytes; %{ @@ -95,10 +109,505 @@ static bool idaapi py_testf_cb(flags_t flags, void *ud) // Wraps the (next|prev)that() static ea_t py_npthat(ea_t ea, ea_t bound, PyObject *py_callable, bool next) { - if (!PyCallable_Check(py_callable)) + if ( !PyCallable_Check(py_callable) ) return BADADDR; return (next ? nextthat : prevthat)(ea, bound, py_testf_cb, py_callable); } + + + +//------------------------------------------------------------------------ +class py_custom_data_type_t +{ + data_type_t dt; + qstring name, menu_name, hotkey, asm_keyword; + int dtid; // The data format id + PyObject *py_self; // Associated Python object + + // may create data? NULL means always may + static bool idaapi s_may_create_at( + void *ud, // user-defined data + ea_t ea, // address of the future item + 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)); + PyShowErr(S_MAY_CREATE_AT); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + // !=NULL means variable size datatype + static asize_t idaapi s_calc_item_size( + // This function is used to determine + // size of the (possible) item at 'ea' + void *ud, // user-defined data + ea_t ea, // address of the item + asize_t maxsize) // maximal size of the item + { + // 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 ( PyShowErr(S_CALC_ITEM_SIZE) || py_result == NULL ) + return 0; + uint64 num = 0; + PyGetNumber(py_result, &num); + Py_XDECREF(py_result); + return asize_t(num); + } + +public: + const char *get_name() const { return name.c_str(); } + py_custom_data_type_t() + { + dtid = -1; + py_self = NULL; + } + + int register_dt(PyObject *py_obj) + { + // Already registered? + if ( dtid >= 0 ) + return dtid; + + memset(&dt, 0, sizeof(dt)); + dt.cbsize = sizeof(dt); + dt.ud = this; + PyObject *py_attr = NULL; + do + { + // name + py_attr = PyObject_TryGetAttrString(py_obj, S_NAME); + if ( py_attr == NULL || !PyString_Check(py_attr) ) + break; + name = PyString_AsString(py_attr); + dt.name = name.c_str(); + Py_DECREF(py_attr); + + // value_size + py_attr = PyObject_TryGetAttrString(py_obj, S_VALUE_SIZE); + if ( py_attr != NULL ) + dt.value_size = PyInt_AsLong(py_attr); + Py_XDECREF(py_attr); + + // props + py_attr = PyObject_TryGetAttrString(py_obj, S_PROPS); + if ( py_attr != NULL ) + dt.props = PyInt_AsLong(py_attr); + Py_XDECREF(py_attr); + + // menu_name + py_attr = PyObject_TryGetAttrString(py_obj, S_MENU_NAME); + if ( py_attr != NULL && PyString_Check(py_attr) ) + { + menu_name = PyString_AsString(py_attr); + dt.menu_name = menu_name.c_str(); + } + Py_XDECREF(py_attr); + + // asm_keyword + py_attr = PyObject_TryGetAttrString(py_obj, S_ASM_KEYWORD); + if ( py_attr != NULL && PyString_Check(py_attr) ) + { + asm_keyword = PyString_AsString(py_attr); + dt.asm_keyword = asm_keyword.c_str(); + } + Py_XDECREF(py_attr); + + // hotkey + py_attr = PyObject_TryGetAttrString(py_obj, S_HOTKEY); + if ( py_attr != NULL && PyString_Check(py_attr) ) + { + hotkey = PyString_AsString(py_attr); + dt.hotkey = hotkey.c_str(); + } + Py_XDECREF(py_attr); + + // may_create_at + py_attr = PyObject_TryGetAttrString(py_obj, S_MAY_CREATE_AT); + if ( py_attr != NULL && PyCallable_Check(py_attr) ) + dt.may_create_at = s_may_create_at; + Py_XDECREF(py_attr); + + // calc_item_size + py_attr = PyObject_TryGetAttrString(py_obj, S_CALC_ITEM_SIZE); + if ( py_attr != NULL && PyCallable_Check(py_attr) ) + dt.calc_item_size = s_calc_item_size; + Py_XDECREF(py_attr); + + // Clear attribute + py_attr = NULL; + + // Now try to register + dtid = register_custom_data_type(&dt); + if ( dtid < 0 ) + break; + + // Hold reference to the PyObject + Py_INCREF(py_obj); + py_self = py_obj; + + py_attr = PyInt_FromLong(dtid); + PyObject_SetAttrString(py_obj, S_ID, py_attr); + Py_DECREF(py_attr); + + // Done with attribute + py_attr = NULL; + } while ( false ); + Py_XDECREF(py_attr); + return dtid; + } + + bool unregister_dt() + { + if ( dtid < 0 ) + return true; + + if ( !unregister_custom_data_type(dtid) ) + return false; + + // Release reference of Python object + Py_XDECREF(py_self); + py_self = NULL; + dtid = -1; + return true; + } + + ~py_custom_data_type_t() + { + unregister_dt(); + } +}; +typedef std::map py_custom_data_type_map_t; +static py_custom_data_type_map_t py_dt_map; + +//------------------------------------------------------------------------ +class py_custom_data_format_t +{ +private: + data_format_t df; + int dfid; + PyObject *py_self; + qstring name, menu_name, hotkey; + + static bool idaapi s_print( // convert to colored string + void *ud, // user-defined data + qstring *out, // output buffer. may be NULL + const void *value, // value to print. may not be NULL + asize_t size, // size of value in bytes + ea_t current_ea, // current address (BADADDR if unknown) + int operand_num, // current operand number + int dtid) // custom data type id + { + // Build a string from the buffer + 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; + PyObject *py_result = PyObject_CallMethod( + _this->py_self, + (char *)S_PRINTF, + "O" PY_FMT64 "ii", + py_value, + pyul_t(current_ea), + operand_num, + dtid); + // Done with the string + Py_DECREF(py_value); + + // Error while calling the function? + if ( PyShowErr(S_PRINTF) || py_result == NULL ) + return false; + + bool ok = false; + if ( PyString_Check(py_result) ) + { + Py_ssize_t len; + char *buf; + if ( out != NULL && PyString_AsStringAndSize(py_result, &buf, &len) != -1 ) + { + out->qclear(); + out->append(buf, len); + } + ok = true; + } + Py_DECREF(py_result); + return ok; + } + + static bool idaapi s_scan( // convert from uncolored string + void *ud, // user-defined data + bytevec_t *value, // output buffer. may be NULL + const char *input, // input string. may not be NULL + ea_t current_ea, // current address (BADADDR if unknown) + int operand_num, // current operand number (-1 if unknown) + qstring *errstr) // buffer for error message + { + py_custom_data_format_t *_this = (py_custom_data_format_t *) ud; + PyObject *py_result = PyObject_CallMethod( + _this->py_self, + (char *)S_SCAN, + "s" PY_FMT64, + input, + pyul_t(current_ea), + operand_num); + + // Error while calling the function? + if ( PyShowErr(S_SCAN) || py_result == NULL) + return false; + + bool ok = false; + do + { + // 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); + + // Get return code from Python + ok = PyObject_IsTrue(py_bool); + + // We expect None or the value (depending on probe) + if ( ok ) + { + // Probe-only? Then okay, no need to extract the 'value' + if ( value == NULL ) + break; + + Py_ssize_t len; + char *buf; + if ( PyString_AsStringAndSize(py_val, &buf, &len) != -1 ) + { + value->qclear(); + value->append(buf, len); + } + } + // An error occured? + else + { + // Make sure the user returned (False, String) + if ( py_bool != Py_False || !PyString_Check(py_val) ) + { + *errstr = "Invalid return value returned from the Python callback!"; + break; + } + // Get the error message + *errstr = PyString_AsString(py_val); + } + } while ( false ); + Py_DECREF(py_result); + return ok; + } + + static void idaapi s_analyze( // analyze custom data format occurrence + void *ud, // user-defined data + ea_t current_ea, // current address (BADADDR if unknown) + int operand_num) // current operand number + // this callback can be used to create + // xrefs from the current item. + // 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); + PyShowErr(S_ANALYZE); + Py_XDECREF(py_result); + } +public: + py_custom_data_format_t() + { + dfid = -1; + py_self = NULL; + } + + const char *get_name() const { return name.c_str(); } + + int register_df(int dtid, PyObject *py_obj) + { + // Already registered? + if ( dfid >= 0 ) + return dfid; + + memset(&df, 0, sizeof(df)); + df.cbsize = sizeof(df); + df.ud = this; + PyObject *py_attr = NULL; + do + { + // name + py_attr = PyObject_TryGetAttrString(py_obj, S_NAME); + if ( py_attr == NULL || !PyString_Check(py_attr) ) + break; + name = PyString_AsString(py_attr); + df.name = name.c_str(); + Py_DECREF(py_attr); + + // menu_name + py_attr = PyObject_TryGetAttrString(py_obj, S_MENU_NAME); + if ( py_attr != NULL && PyString_Check(py_attr) ) + { + menu_name = PyString_AsString(py_attr); + df.menu_name = menu_name.c_str(); + } + Py_XDECREF(py_attr); + + // props + py_attr = PyObject_TryGetAttrString(py_obj, S_PROPS); + if ( py_attr != NULL ) + df.props = PyInt_AsLong(py_attr); + Py_XDECREF(py_attr); + + // hotkey + py_attr = PyObject_TryGetAttrString(py_obj, S_HOTKEY); + if ( py_attr != NULL && PyString_Check(py_attr) ) + { + hotkey = PyString_AsString(py_attr); + df.hotkey = hotkey.c_str(); + } + Py_XDECREF(py_attr); + + // value_size + py_attr = PyObject_TryGetAttrString(py_obj, S_VALUE_SIZE); + if ( py_attr != NULL ) + df.value_size = PyInt_AsLong(py_attr); + Py_XDECREF(py_attr); + + // text_width + py_attr = PyObject_TryGetAttrString(py_obj, S_TEXT_WIDTH); + if ( py_attr != NULL ) + df.text_width = PyInt_AsLong(py_attr); + Py_XDECREF(py_attr); + + // print cb + py_attr = PyObject_TryGetAttrString(py_obj, S_PRINTF); + if ( py_attr != NULL && PyCallable_Check(py_attr) ) + df.print = s_print; + Py_XDECREF(py_attr); + + // scan cb + py_attr = PyObject_TryGetAttrString(py_obj, S_SCAN); + if ( py_attr != NULL && PyCallable_Check(py_attr) ) + df.scan = s_scan; + Py_XDECREF(py_attr); + + // analyze cb + py_attr = PyObject_TryGetAttrString(py_obj, S_ANALYZE); + if ( py_attr != NULL && PyCallable_Check(py_attr) ) + df.analyze = s_analyze; + Py_XDECREF(py_attr); + + // Done with attribute + py_attr = NULL; + + // Now try to register + dfid = register_custom_data_format(dtid, &df); + if ( dfid < 0 ) + break; + + // Hold reference to the PyObject + Py_INCREF(py_obj); + py_self = py_obj; + + py_attr = PyInt_FromLong(dfid); + PyObject_SetAttrString(py_obj, S_ID, py_attr); + Py_DECREF(py_attr); + py_attr = NULL; + } while ( false ); + Py_XDECREF(py_attr); + return dfid; + } + + bool unregister_df(int dtid) + { + // Never registered? + if ( dfid < 0 ) + return true; + + if ( !unregister_custom_data_format(dtid, dfid) ) + return false; + + // Release reference of Python object + Py_XDECREF(py_self); + py_self = NULL; + dfid = -1; + return true; + } + + ~py_custom_data_format_t() + { + } +}; + +//------------------------------------------------------------------------ +// Helper class to bind pairs to py_custom_data_format_t +class py_custom_data_format_list_t +{ + struct py_custom_data_format_entry_t + { + int dtid; + int dfid; + py_custom_data_format_t *df; + }; + typedef qvector ENTRY; + ENTRY entries; +public: + typedef ENTRY::iterator POS; + void add(int dtid, int dfid, py_custom_data_format_t *df) + { + py_custom_data_format_entry_t &e = entries.push_back(); + e.dtid = dtid; + e.dfid = dfid; + e.df = df; + } + py_custom_data_format_t *find(int dtid, int dfid, POS *loc = NULL) + { + for ( POS it=entries.begin(), it_end = entries.end(); it!=it_end; ++it ) + { + if ( it->dfid == dfid && it->dtid == dtid ) + { + if ( loc != NULL ) + *loc = it; + return it->df; + } + } + return NULL; + } + void erase(POS &pos) + { + entries.erase(pos); + } +}; +static py_custom_data_format_list_t py_df_list; + +//------------------------------------------------------------------------ +static PyObject *py_data_type_to_py_dict(const data_type_t *dt) +{ + return Py_BuildValue("{s:" PY_FMT64 ",s:i,s:i,s:s,s:s,s:s,s:s}", + S_VALUE_SIZE, pyul_t(dt->value_size), + S_PROPS, dt->props, + S_CBSIZE, dt->cbsize, + S_NAME, dt->name == NULL ? "" : dt->name, + S_MENU_NAME, dt->menu_name == NULL ? "" : dt->menu_name, + S_HOTKEY, dt->hotkey == NULL ? "" : dt->hotkey, + S_ASM_KEYWORD, dt->asm_keyword == NULL ? "" : dt->asm_keyword); +} + +//------------------------------------------------------------------------ +static PyObject *py_data_format_to_py_dict(const data_format_t *df) +{ + return Py_BuildValue("{s:i,s:i,s:i,s:" PY_FMT64 ",s:s,s:s,s:s}", + S_PROPS, df->props, + S_CBSIZE, df->cbsize, + S_TEXT_WIDTH, df->text_width, + S_VALUE_SIZE, pyul_t(df->value_size), + S_NAME, df->name == NULL ? "" : df->name, + S_MENU_NAME, df->menu_name == NULL ? "" : df->menu_name, + S_HOTKEY, df->hotkey == NULL ? "" : df->hotkey); +} // %} @@ -117,34 +626,441 @@ static ea_t py_prevthat(ea_t ea, ea_t minea, PyObject *callable) } //------------------------------------------------------------------------ -// Get the specified number of bytes of the program into the buffer. -static PyObject *py_get_many_bytes(ea_t ea, int size) +/* +# +def get_many_bytes(ea): + """ + Get the specified number of bytes of the program into the buffer. + @param ea: program address + @param size: number of bytes to return + @return: None or the string buffer + """ + pass +# +*/ +static PyObject *py_get_many_bytes(ea_t ea, unsigned int size) { - do + do { - if (size <= 0) + if ( size <= 0 ) break; - // Allocate memory - char *buf = (char *) qalloc(size); - if (buf == NULL) + + // Allocate memory via Python + PyObject *py_buf = PyString_FromStringAndSize(NULL, Py_ssize_t(size)); + if ( py_buf == NULL ) break; // Read bytes - bool ok = get_many_bytes(ea, buf, size); + bool ok = get_many_bytes(ea, PyString_AsString(py_buf), size); - // If ok, create a python string - PyObject *py_buf; - if (ok) - py_buf = PyString_FromStringAndSize(buf, size); + // If failed, dispose the Python string + if ( !ok ) + { + Py_DECREF(py_buf); - // Free buffer - qfree(buf); - - // Return buffer to Python - if (ok) - return py_buf; - } while (false); + py_buf = Py_None; + Py_INCREF(py_buf); + } + + return py_buf; + } while ( false ); Py_RETURN_NONE; } + + + + +//------------------------------------------------------------------------ +/* +# +def register_custom_data_type(dt): + """ + Registers a custom data type. + @param dt: an instance of the data_type_t class + @return: + < 0 if failed to register + > 0 data type id + """ + pass +# +*/ +// Given a py.data_format_t object, this function will register a datatype +static int py_register_custom_data_type(PyObject *py_dt) +{ + py_custom_data_type_t *inst = new py_custom_data_type_t(); + int r = inst->register_dt(py_dt); + if ( r < 0 ) + { + delete inst; + return r; + } + // Insert the instance to the map + py_dt_map[r] = inst; + return r; +} + +//------------------------------------------------------------------------ +/* +# +def unregister_custom_data_type(dtid): + """ + Unregisters a custom data type. + @param dtid: the data type id + @return: Boolean + """ + pass +# +*/ +static bool py_unregister_custom_data_type(int dtid) +{ + py_custom_data_type_map_t::iterator it = py_dt_map.find(dtid); + + // Maybe the user is trying to unregister a C api dt? + if ( it == py_dt_map.end() ) + return unregister_custom_data_type(dtid); + + py_custom_data_type_t *inst = it->second; + bool ok = inst->unregister_dt(); + + // Perhaps it was automatically unregistered because the idb was close? + if ( !ok ) + { + // Is this type still registered with IDA? + // If not found then mark the context for deletion + ok = find_custom_data_type(inst->get_name()) < 0; + } + + if ( ok ) + { + py_dt_map.erase(it); + delete inst; + } + return ok; +} + +//------------------------------------------------------------------------ +/* +# +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: + < 0 if failed to register + > 0 data format id + """ + pass +# +*/ +static int py_register_custom_data_format(int dtid, PyObject *py_df) +{ + py_custom_data_format_t *inst = new py_custom_data_format_t(); + int r = inst->register_df(dtid, py_df); + if ( r < 0 ) + { + delete inst; + return r; + } + // Insert the instance + py_df_list.add(dtid, r, inst); + return r; +} + +//------------------------------------------------------------------------ +/* +# +def unregister_custom_data_format(dtid, dfid): + """ + Unregisters a custom data format + @param dtid: data type id + @param dfid: data format id + @return: Boolean + """ + pass +# +*/ +static bool py_unregister_custom_data_format(int dtid, int dfid) +{ + py_custom_data_format_list_t::POS pos; + py_custom_data_format_t *inst = py_df_list.find(dtid, dfid, &pos); + // Maybe the user is trying to unregister a C api data format? + if ( inst == NULL ) + return unregister_custom_data_format(dtid, dfid); + + bool ok = inst->unregister_df(dtid); + + // Perhaps it was automatically unregistered because the type was unregistered? + if ( !ok ) + { + // Is this format still registered with IDA? + // If not, mark the context for deletion + ok = find_custom_data_format(inst->get_name()) < 0; + } + + if ( ok ) + { + py_df_list.erase(pos); + delete inst; + } + return ok; +} + +//------------------------------------------------------------------------ +/* +# +def get_custom_data_format(dtid, dfid): + """ + Returns a dictionary populated with the data format values or None on failure. + @param dtid: data type id + @param dfid: data format id + """ + pass +# +*/ +// Get definition of a registered custom data format and returns a dictionary +static PyObject *py_get_custom_data_format(int dtid, int fid) +{ + const data_format_t *df = get_custom_data_format(dtid, fid); + if ( df == NULL ) + Py_RETURN_NONE; + return py_data_format_to_py_dict(df); +} + +//------------------------------------------------------------------------ +/* +# +def get_custom_data_type(dtid): + """ + Returns a dictionary populated with the data type values or None on failure. + @param dtid: data type id + """ + pass +# +*/ +// Get definition of a registered custom data format and returns a dictionary +static PyObject *py_get_custom_data_type(int dtid) +{ + const data_type_t *dt = get_custom_data_type(dtid); + if ( dt == NULL ) + Py_RETURN_NONE; + return py_data_type_to_py_dict(dt); +} + // %} + +%pythoncode %{ +# +DTP_NODUP = 0x0001 + +class data_type_t(object): + """ + Custom data type definition. All data types should inherit from this class. + """ + + def __init__(self, name, value_size = 0, menu_name = None, hotkey = None, asm_keyword = None, props = 0): + """Please refer to bytes.hpp / data_type_t in the SDK""" + self.name = name + self.props = props + self.menu_name = menu_name + self.hotkey = hotkey + self.asm_keyword = asm_keyword + self.value_size = value_size + + self.id = -1 # Will be initialized after registration + """Contains the data type id after the data type is registered""" + + def register(self): + """Registers the data type and returns the type id or < 0 on failure""" + return _idaapi.register_custom_data_type(self) + + def unregister(self): + """Unregisters the data type and returns True on success""" + # Not registered? + if self.id < 0: + return True + + # Try to unregister + r = _idaapi.unregister_custom_data_type(self.id) + + # Clear the ID + if r: + self.id = -1 + return r +# +# def may_create_at(self, ea, nbytes): +# """ +# (optional) If this callback is not defined then this means always may create data type at the given ea. +# @param ea: address of the future item +# @param nbytes: size of the future item +# @return: Boolean +# """ +# +# return False +# +# def calc_item_size(self, ea, maxsize): +# """ +# (optional) If this callback is defined it means variable size datatype +# This function is used to determine size of the (possible) item at 'ea' +# @param ea: address of the item +# @param maxsize: maximal size of the item +# @return: integer +# Returns: 0-no such item can be created/displayed +# this callback is required only for varsize datatypes +# """ +# return 0 +# +# ----------------------------------------------------------------------- +# Uncomment the corresponding callbacks in the inherited class +class data_format_t(object): + """Information about a data format""" + def __init__(self, name, value_size = 0, menu_name = None, props = 0, hotkey = None, text_width = 0): + """Custom data format definition. + @param name: Format name, must be unique + @param menu_name: Visible format name to use in menus + @param props: properties (currently 0) + @param hotkey: Hotkey for the corresponding menu item + @param value_size: size of the value in bytes. 0 means any size is ok + @text_width: Usual width of the text representation + """ + self.name = name + self.menu_name = menu_name + self.props = props + self.hotkey = hotkey + self.value_size = value_size + self.text_width = text_width + + self.id = -1 # Will be initialized after registration + """contains the format id after the format gets registered""" + + def register(self, dtid): + """Registers the data format with the given data type id and returns the type id or < 0 on failure""" + return _idaapi.register_custom_data_format(dtid, self) + + def unregister(self, dtid): + """Unregisters the data format with the given data type id""" + + # Not registered? + if self.id < 0: + return True + + # Unregister + r = _idaapi.unregister_custom_data_format(dtid, self.id) + + # Clear the ID + if r: + self.id = -1 + return r +# +# def printf(self, value, current_ea, operand_num, dtid): +# """ +# Convert a value buffer to colored string. +# +# @param value: The value to be printed +# @param current_ea: The ea of the value +# @param operand_num: The affected operand +# @param dtid: custom data type id (0-standard built-in data type) +# @return: a colored string representing the passed 'value' or None on failure +# """ +# return None +# +# def scan(self, input, current_ea, operand_num): +# """ +# Convert from uncolored string 'input' to byte value +# +# @param input: input string +# @param current_ea: current address (BADADDR if unknown) +# @param operand_num: current operand number (-1 if unknown) +# +# @return: tuple (Boolean, string) +# - (False, ErrorMessage) if conversion fails +# - (True, Value buffer) if conversion succeeds +# """ +# return (False, "Not implemented") +# +# def analyze(self, current_ea, operand_num): +# """ +# (optional) Analyze custom data format occurrence. +# It can be used to create xrefs from the current item. +# +# @param current_ea: current address (BADADDR if unknown) +# @param operand_num: current operand number +# @return: None +# """ +# +# pass +# +# ----------------------------------------------------------------------- +def __walk_types_and_formats(formats, type_action, format_action): + broken = False + for f in formats: + if len(f) == 1: + if not format_action(f[0], 0): + broken = True + break + else: + dt = f[0] + dfs = f[1:] + if not type_action(dt): + broken = True + break + for df in dfs: + if not format_action(df, dt.id): + broken = True + break + return not broken + +# ----------------------------------------------------------------------- +def register_data_types_and_formats(formats): + """ + Registers multiple data types and formats at once. + To register one type/format at a time use register_custom_data_type/register_custom_data_format + + It employs a special table of types and formats described below: + + The 'formats' is a list of tuples. If a tuple has one element then it is the format to be registered with dtid=0 + If the tuple has more than one element, then tuple[0] is the data type and tuple[1:] are the data formats. For example: + many_formats = [ + (pascal_data_type(), pascal_data_format()), + (simplevm_data_type(), simplevm_data_format()), + (makedword_data_format(),), + (simplevm_data_format(),) + ] + The first two tuples describe data types and their associated formats. + The last two tuples describe two data formats to be used with built-in data types. + """ + def __reg_format(df, dtid): + df.register(dtid) + if dtid == 0: + print "Registering format '%s' with built-in types, ID=%d" % (df.name, df.id) + else: + print " Registering format '%s', ID=%d (dtid=%d)" % (df.name, df.id, dtid) + return df.id != -1 + + def __reg_type(dt): + dt.register() + print "Registering type '%s', ID=%d" % (dt.name, dt.id) + return dt.id != -1 + ok = __walk_types_and_formats(formats, __reg_type, __reg_format) + return 1 if ok else -1 + +# ----------------------------------------------------------------------- +def unregister_data_types_and_formats(formats): + """As opposed to register_data_types_and_formats(), this function + unregisters multiple data types and formats at once. + """ + def __unreg_format(df, dtid): + df.unregister(dtid) + print "%snregistering format '%s'" % ("U" if dtid == 0 else " u", df.name) + return True + + def __unreg_type(dt): + print "Unregistering type '%s', ID=%d" % (dt.name, dt.id) + dt.unregister() + return True + ok = __walk_types_and_formats(formats, __unreg_type, __unreg_format) + return 1 if ok else -1 + +# +%} \ No newline at end of file diff --git a/swig/dbg.i b/swig/dbg.i index 72fb517..8d8c0cf 100644 --- a/swig/dbg.i +++ b/swig/dbg.i @@ -1,263 +1,289 @@ -// SWIG chokes on the original declaration so it is replicated here -typedef struct -{ - ulonglong ival; // 8: integer value - ushort fval[6]; // 12: floating point value in the internal representation (see ieee.h) -} regval_t; - -%ignore dbg; -%ignore get_manual_regions; -%rename (get_manual_regions) py_get_manual_regions; -%ignore set_manual_regions; -%include "dbg.hpp" -%ignore DBG_Callback; -%feature("director") DBG_Hooks; - -%{ -// -static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas); -// -%} - -%inline %{ - -// -//------------------------------------------------------------------------- -static PyObject *py_get_manual_regions() -{ - meminfo_vec_t areas; - get_manual_regions(&areas); - return meminfo_vec_t_to_py(areas); -} - -//------------------------------------------------------------------------- -static PyObject *refresh_debugger_memory() -{ - invalidate_dbgmem_config(); - invalidate_dbgmem_contents(BADADDR, BADADDR); - if ( dbg != NULL && dbg->stopped_at_debug_event != NULL ) - dbg->stopped_at_debug_event(true); - Py_RETURN_NONE; -} -// - -int idaapi DBG_Callback(void *ud, int notification_code, va_list va); -class DBG_Hooks -{ -public: - virtual ~DBG_Hooks() {}; - - bool hook() { return hook_to_notification_point(HT_DBG, DBG_Callback, this); }; - bool unhook() { return unhook_from_notification_point(HT_DBG, DBG_Callback, this); }; - /* Hook functions to be overridden in Python */ - virtual void dbg_process_start(pid_t pid, - thid_t tid, - ea_t ea, - char *name, - ea_t base, - asize_t size) { }; - virtual void dbg_process_exit(pid_t pid, - thid_t tid, - ea_t ea, - int exit_code) { }; - virtual void dbg_process_attach(pid_t pid, - thid_t tid, - ea_t ea, - char *name, - ea_t base, - asize_t size) { }; - virtual void dbg_process_detach(pid_t pid, - thid_t tid, - ea_t ea) { }; - virtual void dbg_thread_start(pid_t pid, - thid_t tid, - ea_t ea) { }; - virtual void dbg_thread_exit(pid_t pid, - thid_t tid, - ea_t ea, - int exit_code) { }; - virtual void dbg_library_load(pid_t pid, - thid_t tid, - ea_t ea, - char *name, - ea_t base, - asize_t size) { }; - virtual void dbg_library_unload(pid_t pid, - thid_t tid, - ea_t ea, - char *libname) { }; - virtual void dbg_information(pid_t pid, - thid_t tid, - ea_t ea, - char *info) { }; - virtual int dbg_exception(pid_t pid, - thid_t tid, - ea_t ea, - int code, - bool can_cont, - ea_t exc_ea, - char *info) { return 0; }; - virtual void dbg_suspend_process(void) { }; - virtual int dbg_bpt(thid_t tid, ea_t breakpoint_ea) { return 0; }; - virtual int dbg_trace(thid_t tid, ea_t ip) { return 0; }; - virtual void dbg_request_error(int failed_command, - int failed_dbg_notification) { }; - virtual void dbg_step_into(void) { }; - virtual void dbg_step_over(void) { }; - virtual void dbg_run_to(thid_t tid) { }; - virtual void dbg_step_until_ret(void) { }; -}; - -int idaapi DBG_Callback(void *ud, int notification_code, va_list va) -{ - class DBG_Hooks *proxy = (class DBG_Hooks *)ud; - - debug_event_t *event; - thid_t tid; - int *warn; - ea_t ip; - ea_t breakpoint_ea; - - try { - switch (notification_code) - { - case dbg_process_start: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_start(event->pid, - event->tid, - event->ea, - event->modinfo.name, - event->modinfo.base, - event->modinfo.size); - return 0; - case dbg_process_exit: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_exit(event->pid, - event->tid, - event->ea, - event->exit_code); - return 0; - - case dbg_process_attach: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_attach(event->pid, - event->tid, - event->ea, - event->modinfo.name, - event->modinfo.base, - event->modinfo.size); - return 0; - - case dbg_process_detach: - event = va_arg(va, debug_event_t *); - proxy->dbg_process_detach(event->pid, - event->tid, - event->ea); - return 0; - - case dbg_thread_start: - event = va_arg(va, debug_event_t *); - proxy->dbg_thread_start(event->pid, - event->tid, - event->ea); - return 0; - - case dbg_thread_exit: - event = va_arg(va, debug_event_t *); - proxy->dbg_thread_exit(event->pid, - event->tid, - event->ea, - event->exit_code); - return 0; - - case dbg_library_load: - event = va_arg(va, debug_event_t *); - proxy->dbg_library_load(event->pid, - event->tid, - event->ea, - event->modinfo.name, - event->modinfo.base, - event->modinfo.size); - return 0; - - case dbg_library_unload: - event = va_arg(va, debug_event_t *); - proxy->dbg_library_unload(event->pid, - event->tid, - event->ea, - event->info); - return 0; - - case dbg_information: - event = va_arg(va, debug_event_t *); - proxy->dbg_information(event->pid, - event->tid, - event->ea, - event->info); - return 0; - - case dbg_exception: - event = va_arg(va, debug_event_t *); - warn = va_arg(va, int *); - *warn = proxy->dbg_exception(event->pid, - event->tid, - event->ea, - event->exc.code, - event->exc.can_cont, - event->exc.ea, - event->exc.info); - return 0; - - case dbg_suspend_process: - proxy->dbg_suspend_process(); - return 0; - - case dbg_bpt: - tid = va_arg(va, thid_t); - breakpoint_ea = va_arg(va, ea_t); - warn = va_arg(va, int *); - *warn = proxy->dbg_bpt(tid, breakpoint_ea); - return 0; - - case dbg_trace: - tid = va_arg(va, thid_t); - ip = va_arg(va, ea_t); - return proxy->dbg_bpt(tid, ip); - - case dbg_request_error: - { - int failed_command = (int)va_arg(va, ui_notification_t); - int failed_dbg_notification = (int)va_arg(va, dbg_notification_t); - proxy->dbg_request_error(failed_command, failed_dbg_notification); - return 0; - } - case dbg_step_into: - proxy->dbg_step_into(); - return 0; - - case dbg_step_over: - proxy->dbg_step_over(); - return 0; - - case dbg_run_to: - tid = va_arg(va, thid_t); - proxy->dbg_run_to(tid); - return 0; - - case dbg_step_until_ret: - proxy->dbg_step_until_ret(); - return 0; - } - } - catch (Swig::DirectorException &) - { - msg("Exception in IDP Hook function:\n"); - if (PyErr_Occurred()) - { - PyErr_Print(); - } - } - return 0; -} - -%} +// SWIG chokes on the original declaration so it is replicated here +typedef struct +{ + ulonglong ival; // 8: integer value + ushort fval[6]; // 12: floating point value in the internal representation (see ieee.h) +} regval_t; + +%ignore dbg; +%ignore get_manual_regions; +%ignore source_file_t; +%ignore source_item_t; +%ignore srcinfo_provider_t; +%rename (get_manual_regions) py_get_manual_regions; +%ignore set_manual_regions; +%include "dbg.hpp" +%ignore DBG_Callback; +%feature("director") DBG_Hooks; + +%{ +// +static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas); +// +%} + +%inline %{ + +// + +//------------------------------------------------------------------------- +/* +# +def get_manual_regions(): + """ + Returns the manual memory regions + @return: list(startEA, endEA, name, sclass, sbase, bitness, perm) + """ + pass +# +*/ +static PyObject *py_get_manual_regions() +{ + meminfo_vec_t areas; + get_manual_regions(&areas); + return meminfo_vec_t_to_py(areas); +} + +//------------------------------------------------------------------------- +/* +# +def refresh_debugger_memory(): + """ + Refreshes the debugger memory + @return: Nothing + """ + pass +# +*/ +static PyObject *refresh_debugger_memory() +{ + invalidate_dbgmem_config(); + invalidate_dbgmem_contents(BADADDR, 0); + if ( dbg != NULL && dbg->stopped_at_debug_event != NULL ) + dbg->stopped_at_debug_event(true); + isEnabled(0); + + Py_RETURN_NONE; +} +// + +int idaapi DBG_Callback(void *ud, int notification_code, va_list va); +class DBG_Hooks +{ +public: + virtual ~DBG_Hooks() {}; + + bool hook() { return hook_to_notification_point(HT_DBG, DBG_Callback, this); }; + bool unhook() { return unhook_from_notification_point(HT_DBG, DBG_Callback, this); }; + /* Hook functions to be overridden in Python */ + virtual void dbg_process_start(pid_t pid, + thid_t tid, + ea_t ea, + char *name, + ea_t base, + asize_t size) { }; + virtual void dbg_process_exit(pid_t pid, + thid_t tid, + ea_t ea, + int exit_code) { }; + virtual void dbg_process_attach(pid_t pid, + thid_t tid, + ea_t ea, + char *name, + ea_t base, + asize_t size) { }; + virtual void dbg_process_detach(pid_t pid, + thid_t tid, + ea_t ea) { }; + virtual void dbg_thread_start(pid_t pid, + thid_t tid, + ea_t ea) { }; + virtual void dbg_thread_exit(pid_t pid, + thid_t tid, + ea_t ea, + int exit_code) { }; + virtual void dbg_library_load(pid_t pid, + thid_t tid, + ea_t ea, + char *name, + ea_t base, + asize_t size) { }; + virtual void dbg_library_unload(pid_t pid, + thid_t tid, + ea_t ea, + char *libname) { }; + virtual void dbg_information(pid_t pid, + thid_t tid, + ea_t ea, + char *info) { }; + virtual int dbg_exception(pid_t pid, + thid_t tid, + ea_t ea, + int code, + bool can_cont, + ea_t exc_ea, + char *info) { return 0; }; + virtual void dbg_suspend_process(void) { }; + virtual int dbg_bpt(thid_t tid, ea_t breakpoint_ea) { return 0; }; + virtual int dbg_trace(thid_t tid, ea_t ip) { return 0; }; + virtual void dbg_request_error(int failed_command, + int failed_dbg_notification) { }; + virtual void dbg_step_into(void) { }; + virtual void dbg_step_over(void) { }; + virtual void dbg_run_to(thid_t tid) { }; + virtual void dbg_step_until_ret(void) { }; +}; + +int idaapi DBG_Callback(void *ud, int notification_code, va_list va) +{ + class DBG_Hooks *proxy = (class DBG_Hooks *)ud; + + debug_event_t *event; + thid_t tid; + int *warn; + ea_t ip; + ea_t breakpoint_ea; + + try { + switch (notification_code) + { + case dbg_process_start: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_start(event->pid, + event->tid, + event->ea, + event->modinfo.name, + event->modinfo.base, + event->modinfo.size); + return 0; + case dbg_process_exit: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_exit(event->pid, + event->tid, + event->ea, + event->exit_code); + return 0; + + case dbg_process_attach: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_attach(event->pid, + event->tid, + event->ea, + event->modinfo.name, + event->modinfo.base, + event->modinfo.size); + return 0; + + case dbg_process_detach: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_detach(event->pid, + event->tid, + event->ea); + return 0; + + case dbg_thread_start: + event = va_arg(va, debug_event_t *); + proxy->dbg_thread_start(event->pid, + event->tid, + event->ea); + return 0; + + case dbg_thread_exit: + event = va_arg(va, debug_event_t *); + proxy->dbg_thread_exit(event->pid, + event->tid, + event->ea, + event->exit_code); + return 0; + + case dbg_library_load: + event = va_arg(va, debug_event_t *); + proxy->dbg_library_load(event->pid, + event->tid, + event->ea, + event->modinfo.name, + event->modinfo.base, + event->modinfo.size); + return 0; + + case dbg_library_unload: + event = va_arg(va, debug_event_t *); + proxy->dbg_library_unload(event->pid, + event->tid, + event->ea, + event->info); + return 0; + + case dbg_information: + event = va_arg(va, debug_event_t *); + proxy->dbg_information(event->pid, + event->tid, + event->ea, + event->info); + return 0; + + case dbg_exception: + event = va_arg(va, debug_event_t *); + warn = va_arg(va, int *); + *warn = proxy->dbg_exception(event->pid, + event->tid, + event->ea, + event->exc.code, + event->exc.can_cont, + event->exc.ea, + event->exc.info); + return 0; + + case dbg_suspend_process: + proxy->dbg_suspend_process(); + return 0; + + case dbg_bpt: + tid = va_arg(va, thid_t); + breakpoint_ea = va_arg(va, ea_t); + warn = va_arg(va, int *); + *warn = proxy->dbg_bpt(tid, breakpoint_ea); + return 0; + + case dbg_trace: + tid = va_arg(va, thid_t); + ip = va_arg(va, ea_t); + return proxy->dbg_bpt(tid, ip); + + case dbg_request_error: + { + int failed_command = (int)va_argi(va, ui_notification_t); + int failed_dbg_notification = (int)va_argi(va, dbg_notification_t); + proxy->dbg_request_error(failed_command, failed_dbg_notification); + return 0; + } + case dbg_step_into: + proxy->dbg_step_into(); + return 0; + + case dbg_step_over: + proxy->dbg_step_over(); + return 0; + + case dbg_run_to: + tid = va_arg(va, thid_t); + proxy->dbg_run_to(tid); + return 0; + + case dbg_step_until_ret: + proxy->dbg_step_until_ret(); + return 0; + } + } + catch (Swig::DirectorException &) + { + msg("Exception in IDP Hook function:\n"); + if (PyErr_Occurred()) + { + PyErr_Print(); + } + } + return 0; +} + +%} diff --git a/swig/diskio.i b/swig/diskio.i index 82ebabf..7387b2c 100644 --- a/swig/diskio.i +++ b/swig/diskio.i @@ -60,6 +60,96 @@ int idaapi py_enumerate_files_cb(const char *file, void *ud) %inline %{ // +/* +# +class loader_input_t(pyidc_opaque_object_t): + """A helper class to work with linput_t related functions. + This class is also used by file loaders scripts. + """ + def __init__(self): + pass + + def close(self): + """Closes the file""" + pass + + def open(self, filename, remote = False): + """Opens a file (or a remote file) + @return: Boolean + """ + pass + + def set_linput(self, linput): + """Links the current loader_input_t instance to a linput_t instance""" + pass + + @staticmethod + def from_fp(fp): + """A static method to construct an instance from a FILE*""" + pass + + def open_memory(self, start, size): + """Create a linput for process memory (By internally calling idaapi.create_memory_linput()) + This linput will use dbg->read_memory() to read data + @param start: starting address of the input + @param size: size of the memory area to represent as linput + if unknown, may be passed as 0 + """ + pass + + def seek(self, pos, whence = SEEK_SET): + """Set input source position + @return: the new position (not 0 as fseek!) + """ + pass + + def tell(self): + """Returns the current position""" + pass + + def getz(self, sz, fpos = -1): + """Returns a zero terminated string at the given position + @param sz: maximum size of the string + @param fpos: if != -1 then seek will be performed before reading + @return: The string or None on failure. + """ + pass + + def gets(self, len): + """Reads a line from the input file. Returns the read line or None""" + pass + + def read(self, size): + """Reads from the file. Returns the buffer or None""" + pass + + def readbytes(self, size, big_endian): + """Similar to read() but it respect the endianness""" + pass + + def file2base(self, pos, ea1, ea2, patchable): + """ + Load portion of file into the database + This function will include (ea1..ea2) into the addressing space of the + program (make it enabled) + @param li: pointer ot input source + @param pos: position in the file + @param (ea1..ea2): range of destination linear addresses + @param patchable: should the kernel remember correspondance of + file offsets to linear addresses. + @return: 1-ok,0-read error, a warning is displayed + """ + pass + + def get_char(self): + """Reads a single character from the file. Returns None if EOF or the read character""" + pass + + def opened(self): + """Checks if the file is opened or not""" + pass +# +*/ class loader_input_t { private: @@ -126,17 +216,17 @@ public: } //-------------------------------------------------------------------------- - PyObject *open(const char *filename, bool remote = false) + bool open(const char *filename, bool remote = false) { close(); li = open_linput(filename, remote); if ( li == NULL ) - Py_RETURN_FALSE; + return false; // Save file name fn = filename; own = OWN_CREATE; - Py_RETURN_TRUE; + return true; } //-------------------------------------------------------------------------- @@ -188,16 +278,16 @@ public: } //-------------------------------------------------------------------------- - PyObject *open_memory(ea_t start, asize_t size = 0) + bool open_memory(ea_t start, asize_t size = 0) { linput_t *l = create_memory_linput(start, size); if ( l == NULL ) - Py_RETURN_FALSE; + return false; close(); li = l; fn = ""; own = OWN_CREATE; - Py_RETURN_TRUE; + return true; } //-------------------------------------------------------------------------- @@ -326,6 +416,23 @@ public: //-------------------------------------------------------------------------- +/* +# +def enumerate_files(path, fname, callback): + """ + Enumerate files in the specified directory while the callback returns 0. + @param path: directory to enumerate files in + @param fname: mask of file names to enumerate + @param callback: a callable object that takes the filename as + its first argument and it returns 0 to continue + enumeration or non-zero to stop enumeration. + @return: + None in case of script errors + tuple(code, fname) : If the callback returns non-zero + """ + pass +# +*/ PyObject *py_enumerate_files(PyObject *path, PyObject *fname, PyObject *callback) { do @@ -349,6 +456,7 @@ PyObject *py_enumerate_files(PyObject *path, PyObject *fname, PyObject *callback %pythoncode %{ # def enumerate_system_files(subdir, fname, callback): + """Similar to enumerate_files() however it searches inside IDA directory or its subdirectories""" return enumerate_files(idadir(subdir), fname, callback) # %} diff --git a/swig/enum.i b/swig/enum.i index 717a3de..83d4745 100644 --- a/swig/enum.i +++ b/swig/enum.i @@ -1,29 +1,29 @@ -// Kernel only & unexported symbols -%ignore enums; -%ignore init_enums; -%ignore save_enums; -%ignore term_enums; -%ignore set_enum_flag; -%ignore sync_from_enum;; -%ignore del_all_consts; -%ignore get_selected_enum; -%ignore add_selected_enum; -%ignore unmark_selected_enums; -%ignore is_good_bmask; -%ignore get_bmask_enum; -%ignore ENUM_REVERSE; -%ignore ENUM_SELMEMS; -%ignore ENUM_QTY_IDX; -%ignore ENUM_FLG_IDX; -%ignore ENUM_FLAGS; -%ignore ENUM_FLAGS_IS_BF; -%ignore ENUM_FLAGS_HIDDEN; -%ignore ENUM_MASKS; -%ignore ENUM_MEMBERS; -%ignore CONST_ENUM; -%ignore CONST_VALUE; -%ignore CONST_BMASK; -%ignore CONST_SERIAL; -%ignore CONST_SERIALS; - -%include "enum.hpp" +// Kernel only & unexported symbols +%ignore enums; +%ignore init_enums; +%ignore save_enums; +%ignore term_enums; +%ignore set_enum_flag; +%ignore sync_from_enum;; +%ignore del_all_enum_members; +%ignore get_selected_enum; +%ignore add_selected_enum; +%ignore unmark_selected_enums; +%ignore is_good_bmask; +%ignore get_bmask_enum; +%ignore ENUM_REVERSE; +%ignore ENUM_SELMEMS; +%ignore ENUM_QTY_IDX; +%ignore ENUM_FLG_IDX; +%ignore ENUM_FLAGS; +%ignore ENUM_FLAGS_IS_BF; +%ignore ENUM_FLAGS_HIDDEN; +%ignore ENUM_MASKS; +%ignore ENUM_MEMBERS; +%ignore CONST_ENUM; +%ignore CONST_VALUE; +%ignore CONST_BMASK; +%ignore CONST_SERIAL; +%ignore CONST_SERIALS; + +%include "enum.hpp" diff --git a/swig/expr.i b/swig/expr.i index 5ab1175..18ded5d 100644 --- a/swig/expr.i +++ b/swig/expr.i @@ -7,8 +7,20 @@ %ignore register_extlang; %ignore IDCFuncs; %ignore set_idc_func; +%ignore set_idc_func_ex; %ignore VarLong; %ignore VarNum; +%ignore extlang_get_attr_exists; +%ignore extlang_create_object_exists; +%ignore create_script_object; +%ignore set_script_attr; +%ignore set_attr_exists; +%ignore get_script_attr; +%ignore extlang_get_attr_exists; +%ignore extlang_compile_file; +%ignore get_extlangs; +%ignore create_idc_object; +%ignore run_script_func; %ignore VarString; %ignore VarFloat; %ignore VarFree; diff --git a/swig/fpro.i b/swig/fpro.i index 3e32851..e001f93 100644 --- a/swig/fpro.i +++ b/swig/fpro.i @@ -1,5 +1,79 @@ %inline %{ // +/* +# +class qfile_t(pyidc_opaque_object_t): + """A helper class to work with FILE related functions.""" + def __init__(self): + pass + + def close(self): + """Closes the file""" + pass + + def open(self, filename, mode): + """Opens a file + @param filename: the file name + @param mode: The mode string, ala fopen() style + @return: Boolean + """ + pass + + def set_linput(self, linput): + """Links the current loader_input_t instance to a linput_t instance""" + pass + + @staticmethod + def tmpfile(): + """A static method to construct an instance using a temporary file""" + pass + + def seek(self, pos, whence = SEEK_SET): + """Set input source position + @return: the new position (not 0 as fseek!) + """ + pass + + def tell(self): + """Returns the current position""" + pass + + def gets(self, len): + """Reads a line from the input file. Returns the read line or None""" + pass + + def read(self, size): + """Reads from the file. Returns the buffer or None""" + pass + + def write(self, buf): + """Writes to the file. Returns 0 or the number of bytes written""" + pass + + def readbytes(self, size, big_endian): + """Similar to read() but it respect the endianness""" + pass + + def writebytes(self, size, big_endian): + """Similar to write() but it respect the endianness""" + pass + + def flush(self): + pass + + def get_char(self): + """Reads a single character from the file. Returns None if EOF or the read character""" + pass + + def put_char(self): + """Writes a single character to the file""" + pass + + def opened(self): + """Checks if the file is opened or not""" + pass +# +*/ class qfile_t { private: @@ -71,16 +145,16 @@ public: } //-------------------------------------------------------------------------- - PyObject *open(const char *filename, const char *mode) + bool open(const char *filename, const char *mode) { close(); fp = qfopen(filename, mode); if ( fp == NULL ) - Py_RETURN_FALSE; + return false; // Save file name fn = filename; own = true; - Py_RETURN_TRUE; + return true; } //-------------------------------------------------------------------------- diff --git a/swig/frame.i b/swig/frame.i index c5e7503..f136507 100644 --- a/swig/frame.i +++ b/swig/frame.i @@ -16,5 +16,15 @@ %ignore read_stkpnts; %ignore write_stkpnts; %ignore del_stkpnts; +%ignore rename_frame; + +%ignore get_stkvar; +%rename (get_stkvar) py_get_stkvar; + +%ignore add_stkvar3; +%rename (add_stkvar3) py_add_stkvar3; + +%ignore calc_frame_offset; +%ignore add_stkvar; %include "frame.hpp" diff --git a/swig/funcs.i b/swig/funcs.i index ce63925..b2b5503 100644 --- a/swig/funcs.i +++ b/swig/funcs.i @@ -1,45 +1,51 @@ -%cstring_bounded_output_none(char *buf, MAXSTR); -%cstring_bounded_output_none(char *optlibs, MAXSTR); - -// FIXME: Are these really useful? -%ignore iterate_func_chunks; -%ignore get_idasgn_desc; -%ignore get_idasgn_header_by_short_name; - -// Kernel-only & unexported symbols -%ignore del_regargs; -%ignore write_regargs; -%ignore find_regarg; -%ignore free_regarg; -%ignore determine_rtl; -%ignore init_signatures; -%ignore save_signatures; -%ignore term_signatures; -%ignore init_funcs; -%ignore save_funcs; -%ignore term_funcs; -%ignore move_funcs; -%ignore copy_noret_info; -%ignore recalc_func_noret_flag; -%ignore plan_for_noret_analysis; -%ignore invalidate_sp_analysis; - -%ignore create_func_eas_array; -%ignore auto_add_func_tails; -%ignore read_tails; - -%include "funcs.hpp" - -%clear(char *buf); -%clear(char *optlibs); - -%inline %{ -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]; -} -%} +%cstring_bounded_output_none(char *buf, MAXSTR); +%cstring_bounded_output_none(char *optlibs, MAXSTR); + +// FIXME: Are these really useful? +%ignore iterate_func_chunks; +%ignore get_idasgn_desc; +%ignore get_idasgn_header_by_short_name; + +// Kernel-only & unexported symbols +%ignore del_regargs; +%ignore write_regargs; +%ignore find_regarg; +%ignore free_regarg; +%ignore determine_rtl; +%ignore init_signatures; +%ignore save_signatures; +%ignore term_signatures; +%ignore init_funcs; +%ignore save_funcs; +%ignore term_funcs; +%ignore move_funcs; +%ignore copy_noret_info; +%ignore recalc_func_noret_flag; +%ignore plan_for_noret_analysis; +%ignore invalidate_sp_analysis; + +%ignore create_func_eas_array; +%ignore auto_add_func_tails; +%ignore read_tails; + +%include "funcs.hpp" + +%clear(char *buf); +%clear(char *optlibs); + +%inline %{ +/* +# +def get_fchunk_referer(ea, idx): + pass +# +*/ +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]; +} +%} diff --git a/swig/gdl.i b/swig/gdl.i index 46cbaf8..775bad4 100644 --- a/swig/gdl.i +++ b/swig/gdl.i @@ -1,101 +1,106 @@ -%ignore cancellable_graph_t::check_cancel; -%ignore gdl_graph_t::gen_gdl; -%ignore gdl_graph_t::gen_gdl; -%ignore gdl_graph_t::path; -%ignore gdl_graph_t::path_exists; -%ignore intmap_t::dstr; -%ignore intmap_t::print; -%ignore intseq_t::add_block; -%ignore intseq_t::add_unique; -%ignore intseq_t::del; -%ignore intseq_t::dstr; -%ignore intseq_t::print; -%ignore intseq_t::remove_block; -%ignore intset_t::dstr; -%ignore intset_t::print; -%ignore node_set_t::add; -%ignore node_set_t::extract; -%ignore node_set_t::intersect; -%ignore node_set_t::node_set_t; -%ignore node_set_t::sub; -%ignore qflow_chart_t::blocks; -%ignore flow_chart_t; -%ignore setup_graph_subsystem; -%ignore qbasic_block_t::succ; -%ignore qbasic_block_t::pred; - -%include "gdl.hpp" - -%extend qflow_chart_t -{ - qbasic_block_t *__getitem__(int n) - { - return &(self->blocks[n]); - } -} - -%pythoncode %{ -# -# ----------------------------------------------------------------------- -class BasicBlock: - def __init__(self, id, bb, f): - self._f = f - self.id = id - """Basic block ID""" - self.startEA = bb.startEA - """startEA of basic block""" - self.endEA = bb.endEA - """endEA of basic block""" - self.type = self._f._q.calc_block_type(self.id) - """Block type (check fc_block_type_t enum)""" - - def preds(self): - """ - Iterates the predecessors list - """ - q = self._f._q - for i in xrange(0, self._f._q.npred(self.id)): - yield self._f[q.pred(self.id, i)] - - def succs(self): - """ - Iterates the successors list - """ - q = self._f._q - for i in xrange(0, q.nsucc(self.id)): - yield self._f[q.succ(self.id, i)] - -# ----------------------------------------------------------------------- -class FlowChart: - """ - Flowchart class used to determine basic blocks - """ - def __init__(self, f=None, bounds=None, flags=0): - """ - Constructor - @param f: A func_t type, use get_func(ea) to get a reference - @param bounds: A tuple of the form (start, end). Used if "f" is None - @param flags: one of the FC_xxxx flags. One interesting flag is FC_PREDS - """ - if (not f) and (not bounds or type(bounds) != types.TupleType): - raise Exception("Please specifiy either a function or start/end pair") - if not bounds: - bounds = (BADADDR, BADADDR) - # create the flowchart - self._q = qflow_chart_t("", f, bounds[0], bounds[1], flags) - self.size = self._q.size() - - def refresh(): - self._q.refresh() - self.size = self._q.size() - - def __getitem__(self, index): - """ - Returns a basic block - @return: BasicBlock - """ - if index >= self.size: - raise StopIteration - return BasicBlock(index, self._q[index], self) -# -%} +%ignore cancellable_graph_t::check_cancel; +%ignore gdl_graph_t::gen_gdl; +%ignore gdl_graph_t::gen_gdl; +%ignore gdl_graph_t::path; +%ignore gdl_graph_t::path_exists; +%ignore gdl_graph_t::gen_dot; + +%ignore intmap_t::dstr; +%ignore intmap_t::print; +%ignore intseq_t::add_block; +%ignore intseq_t::add_unique; +%ignore intseq_t::del; +%ignore intseq_t::dstr; +%ignore intseq_t::print; +%ignore intseq_t::remove_block; +%ignore intset_t::dstr; +%ignore intset_t::print; +%ignore node_set_t::add; +%ignore node_set_t::extract; +%ignore node_set_t::intersect; +%ignore node_set_t::node_set_t; +%ignore node_set_t::sub; +%ignore qflow_chart_t::blocks; +%ignore flow_chart_t; +%ignore default_graph_format; +%ignore setup_graph_subsystem; +%ignore qbasic_block_t::succ; +%ignore qbasic_block_t::pred; + +%include "gdl.hpp" + +%extend qflow_chart_t +{ + qbasic_block_t *__getitem__(int n) + { + return &(self->blocks[n]); + } +} + +%pythoncode %{ +# +# ----------------------------------------------------------------------- +class BasicBlock: + """Basic block class. It is returned by the Flowchart class""" + def __init__(self, id, bb, f): + self._f = f + self.id = id + """Basic block ID""" + self.startEA = bb.startEA + """startEA of basic block""" + self.endEA = bb.endEA + """endEA of basic block""" + self.type = self._f._q.calc_block_type(self.id) + """Block type (check fc_block_type_t enum)""" + + def preds(self): + """ + Iterates the predecessors list + """ + q = self._f._q + for i in xrange(0, self._f._q.npred(self.id)): + yield self._f[q.pred(self.id, i)] + + def succs(self): + """ + Iterates the successors list + """ + q = self._f._q + for i in xrange(0, q.nsucc(self.id)): + yield self._f[q.succ(self.id, i)] + +# ----------------------------------------------------------------------- +class FlowChart: + """ + Flowchart class used to determine basic blocks. + Check ex_gdl_qflow_chart.py for sample usage. + """ + def __init__(self, f=None, bounds=None, flags=0): + """ + Constructor + @param f: A func_t type, use get_func(ea) to get a reference + @param bounds: A tuple of the form (start, end). Used if "f" is None + @param flags: one of the FC_xxxx flags. One interesting flag is FC_PREDS + """ + if (not f) and (not bounds or type(bounds) != types.TupleType): + raise Exception("Please specifiy either a function or start/end pair") + if not bounds: + bounds = (BADADDR, BADADDR) + # create the flowchart + self._q = qflow_chart_t("", f, bounds[0], bounds[1], flags) + self.size = self._q.size() + + def refresh(): + self._q.refresh() + self.size = self._q.size() + + def __getitem__(self, index): + """ + Returns a basic block + @return: BasicBlock + """ + if index >= self.size: + raise StopIteration + return BasicBlock(index, self._q[index], self) +# +%} diff --git a/swig/graph.i b/swig/graph.i index 612a2d8..610e678 100644 --- a/swig/graph.i +++ b/swig/graph.i @@ -1,903 +1,918 @@ -%{ -// -class py_graph_t -{ -private: - enum - { - GR_HAVE_USER_HINT = 0x00000001, - GR_HAVE_CLICKED = 0x00000002, - GR_HAVE_DBL_CLICKED = 0x00000004, - GR_HAVE_GOTFOCUS = 0x00000008, - GR_HAVE_LOSTFOCUS = 0x00000010, - GR_HAVE_CHANGED_CURRENT = 0x00000020, - GR_HAVE_CLOSE = 0x00000040, - GR_HAVE_COMMAND = 0x00000080 - }; - struct nodetext_cache_t - { - qstring text; - bgcolor_t bgcolor; - nodetext_cache_t(const nodetext_cache_t &rhs): text(rhs.text), bgcolor(rhs.bgcolor) { } - 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: - nodetext_cache_t *get(int node_id) - { - iterator it = find(node_id); - if (it == end()) - return NULL; - return &it->second; - } - 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); - } - }; - - class tform_pygraph_map_t: public std::map - { - public: - py_graph_t *get(TForm *form) - { - iterator it = find(form); - return it == end() ? NULL : it->second; - } - void add(TForm *form, py_graph_t *py) - { - (*this)[form] = py; - } - }; - class cmdid_map_t: public std::map - { - private: - Py_ssize_t uid; - public: - cmdid_map_t() - { - uid = 1; // we start by one and keep zero for error id - } - void add(py_graph_t *pyg) - { - (*this)[uid] = pyg; - ++uid; - } - const Py_ssize_t id() const { return uid; } - void clear(py_graph_t *pyg) - { - iterator e = end(); - for (iterator it=begin();it!=end();) - { - if (it->second == pyg) - { - iterator temp = it++; - erase(temp); - } - else - ++it; - } - } - py_graph_t *get(Py_ssize_t id) - { - iterator it = find(id); - return it == end() ? NULL : it->second; - } - }; - - static tform_pygraph_map_t tform_pyg; - static cmdid_map_t cmdid_pyg; - int cb_flags; - TForm *form; - graph_viewer_t *gv; - bool refresh_needed; - PyObject *self; - nodetext_cache_map_t node_cache; - - // static callback - static int idaapi s_callback(void *obj, int code, va_list va) - { - return ((py_graph_t *)obj)->gr_callback(code, va); - } - - static bool idaapi s_menucb(void *ud) - { - Py_ssize_t id = (Py_ssize_t)ud; - 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 - PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_COMMAND, "n", id); - Py_XDECREF(ret); - } - - // Refresh user-defined graph node number and edges - // It calls Python method and expects that the user already filled - // the nodes and edges. The nodes and edges are retrieved and passed to IDA - void on_user_refresh(mutable_graph_t *g) - { - if (!refresh_needed) - return; - - // Check return value to OnRefresh() call - PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_REFRESH, NULL); - if ( ret == NULL || !PyObject_IsTrue(ret) ) - { - Py_XDECREF(ret); - return; - } - - // Refer to the nodes - PyObject *nodes = PyObject_TryGetAttrString(self, S_M_NODES); - if (ret == NULL || !PyList_Check(nodes)) - { - Py_XDECREF(nodes); - return; - } - - // Refer to the edges - PyObject *edges = PyObject_TryGetAttrString(self, S_M_EDGES); - if (ret == NULL || !PyList_Check(nodes)) - { - Py_DECREF(nodes); - Py_XDECREF(edges); - return; - } - - // Resize the nodes - int max_nodes = abs(int(PyList_Size(nodes))); - g->clear(); - g->resize(max_nodes); - - // Mark that we refreshed already - refresh_needed = false; - - // Clear cached nodes - node_cache.clear(); - - // Get the edges - for (int i=(int)PyList_Size(edges)-1;i>=0;i--) - { - // Each list item is a sequence (id1, id2) - PyObject *item = PyList_GetItem(edges, i); - if (!PySequence_Check(item)) - continue; - - // Get and validate each of the two elements in the sequence - int edge_ids[2]; - int j; - for (j=0;j 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); - } - Py_DECREF(nodes); - Py_DECREF(edges); - } - - // Retrieves the text for user-defined graph node - // It expects either a string or a tuple (string, bgcolor) - bool on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color) - { - // If already cached then return the value - nodetext_cache_t *c = node_cache.get(node); - if (c != NULL) - { - *str = c->text.c_str(); - if (bg_color != NULL) - *bg_color = c->bgcolor; - return true; - } - - // Not cached, call Python - PyObject *result = PyObject_CallMethod(self, (char *)S_ON_GETTEXT, "i", node); - if (result == NULL) - return false; - - bgcolor_t cl = bg_color == NULL ? DEFCOLOR : *bg_color; - const char *s; - - // User returned a string? - if (PyString_Check(result)) - { - s = PyString_AsString(result); - if (s == NULL) - s = ""; - c = node_cache.add(node, s, cl); - } - // User returned a sequence of text and bgcolor - else if (PySequence_Check(result) && PySequence_Size(result) == 2) - { - PyObject *py_str = PySequence_GetItem(result, 0); - PyObject *py_color = PySequence_GetItem(result, 1); - - if (py_str == NULL || !PyString_Check(py_str) || (s = PyString_AsString(py_str)) == NULL) - s = ""; - if (py_color != NULL && PyNumber_Check(py_color)) - cl = bgcolor_t(PyLong_AsUnsignedLong(py_color)); - - c = node_cache.add(node, s, cl); - - Py_XDECREF(py_str); - Py_XDECREF(py_color); - } - Py_DECREF(result); - - *str = c->text.c_str(); - if (bg_color != NULL) - *bg_color = c->bgcolor; - return true; - } - - // Retrieves the hint for the user-defined graph - // Calls Python and expects a string or None - int on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint) - { - // 'hint' must be allocated by qalloc() or qstrdup() - // out: 0-use default hint, 1-use proposed hint - - // We dispatch hints over nodes only - if (mousenode == -1) - return 0; - - PyObject *result = PyObject_CallMethod(self, (char *)S_ON_HINT, "i", mousenode); - bool ok = result != NULL && PyString_Check(result); - if (!ok) - { - Py_XDECREF(result); - return 0; - } - *hint = qstrdup(PyString_AsString(result)); - Py_DECREF(result); - return 1; // use our hint - } - - // graph is being destroyed - void on_destroy(mutable_graph_t * /*g*/ = NULL) - { - if (self != NULL) - { - if (cb_flags & GR_HAVE_CLOSE) - { - PyObject *result = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL); - Py_XDECREF(result); - } - unbind(); - } - // Remove the TForm from list - if (form != NULL) - tform_pyg.erase(form); - // 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) - { - // in: graph_viewer_t *gv - // selection_item_t *current_item1 - // graph_item_t *current_item2 - // out: 0-ok, 1-ignore click - // this callback allows you to ignore some clicks. - // it occurs too early, internal graph variables are not updated yet - // current_item1, current_item2 point to the same thing - // item2 has more information. - // see also: kernwin.hpp, custom_viewer_click_t - if (item2->n == -1) - return 1; - - PyObject *result = PyObject_CallMethod(self, (char *)S_ON_CLICK, "i", item2->n); - if ( result == NULL || !PyObject_IsTrue(result) ) - { - Py_XDECREF(result); - return 1; - } - - Py_DECREF(result); - - return 0; - } - // a graph node has been double clicked - int on_dblclicked(graph_viewer_t * /*gv*/, selection_item_t *item) - { - // in: graph_viewer_t *gv - // selection_item_t *current_item - // out: 0-ok, 1-ignore click - //graph_viewer_t *v = va_arg(va, graph_viewer_t *); - //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); - if ( result == NULL || !PyObject_IsTrue(result) ) - { - Py_XDECREF(result); - return 1; - } - Py_DECREF(result); - return 0; - } - - // a graph viewer got focus - void on_gotfocus(graph_viewer_t * /*gv*/) - { - PyObject *result = PyObject_CallMethod(self, (char *)S_ON_ACTIVATE, NULL); - 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); - Py_XDECREF(result); - } - - // a new graph node became the current node - int on_changed_current(graph_viewer_t * /*gv*/, int curnode) - { - // in: graph_viewer_t *gv - // int curnode - // 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); - bool allow = (result != NULL && PyObject_IsTrue(result)); - Py_XDECREF(result); - return allow ? 0 : 1; - } - - int gr_callback(int code, va_list va) - { - int ret; - switch (code) - { - // - case grcode_user_text: - { - mutable_graph_t *g = va_arg(va, mutable_graph_t *); - int node = va_arg(va, int); - const char **result = va_arg(va, const char **); - bgcolor_t *bgcolor = va_arg(va, bgcolor_t *); - ret = on_user_text(g, node, result, bgcolor); - break; - } - // - case grcode_destroyed: - on_destroy(va_arg(va, mutable_graph_t *)); - ret = 0; - break; - // - case grcode_clicked: - if (cb_flags & GR_HAVE_CLICKED) - { - graph_viewer_t *gv = va_arg(va, graph_viewer_t *); - selection_item_t *item = va_arg(va, selection_item_t *); - graph_item_t *gitem = va_arg(va, graph_item_t *); - ret = on_clicked(gv, item, gitem); - } - else - ret = 1; // ignore click - break; - // - case grcode_dblclicked: - if (cb_flags & GR_HAVE_DBL_CLICKED) - { - graph_viewer_t *gv = va_arg(va, graph_viewer_t *); - selection_item_t *item = va_arg(va, selection_item_t *); - ret = on_dblclicked(gv, item); - } - else - ret = 1; // ignore - break; - // - 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; - // - case grcode_user_hint: - if (cb_flags & GR_HAVE_USER_HINT) - { - mutable_graph_t *g = va_arg(va, mutable_graph_t *); - int mousenode = va_arg(va, int); - int mouseedge_src = va_arg(va, int); - int mouseedge_dest = va_arg(va, int); - char **hint = va_arg(va, char **); - ret = on_user_hint(g, mousenode, mouseedge_src, mouseedge_dest, hint); - } - else - { - ret = 0; - } - break; - // - case grcode_changed_current: - if (cb_flags & GR_HAVE_CHANGED_CURRENT) - { - graph_viewer_t *gv = va_arg(va, graph_viewer_t *); - int cur_node = va_arg(va, int); - ret = on_changed_current(gv, cur_node); - } - else - ret = 0; // allow selection change - break; - // - default: - ret = 0; - break; - } - //grcode_changed_graph, // new graph has been set - //grcode_creating_group, // a group is being created - //grcode_deleting_group, // a group is being deleted - //grcode_group_visibility, // a group is being collapsed/uncollapsed - //grcode_user_size, // calculate node size for user-defined graph - //grcode_user_title, // render node title of a user-defined graph - //grcode_user_draw, // render node of a user-defined graph - return ret; - } - - void unbind() - { - if (self == NULL) - return; - - // Unbind this object from the python object - PyObject *py_cobj = PyCObject_FromVoidPtr(NULL, NULL); - PyObject_SetAttrString(self, S_M_THIS, py_cobj); - Py_DECREF(py_cobj); - Py_XDECREF(self); - self = NULL; - } - - void show() - { - open_tform(form, FORM_MDI|FORM_TAB|FORM_MENU); - } - - static py_graph_t *extract_this(PyObject *self) - { - // Try to extract "this" from the python object - PyObject *py_this = PyObject_TryGetAttrString(self, S_M_THIS); - if (py_this == NULL || !PyCObject_Check(py_this)) - { - Py_XDECREF(py_this); - return NULL; - } - py_graph_t *ret = (py_graph_t *) PyCObject_AsVoidPtr(py_this); - Py_DECREF(py_this); - return ret; - } - - void jump_to_node(int nid) - { - viewer_center_on(gv, nid); - int x, y; - - // will return a place only when a node was previously selected - place_t *old_pl = get_custom_viewer_place(gv, false, &x, &y); - if ( old_pl != NULL ) - { - user_graph_place_t *new_pl = (user_graph_place_t *) old_pl->clone(); - new_pl->node = nid; - jumpto(gv, new_pl, x, y); - delete new_pl; - } - } - - void refresh() - { - refresh_needed = true; - refresh_viewer(gv); - } - - static bool extract_title(PyObject *self, qstring *title) - { - PyObject *py_title = PyObject_TryGetAttrString(self, S_M_TITLE); - if ( py_title == NULL ) - return false; - *title = PyString_AsString(py_title); - Py_DECREF(py_title); - return true; - } - - int create(PyObject *self, const char *title) - { - // check what callbacks we have - static const struct - { - const char *name; - int have; - } callbacks[] = - { - {S_ON_REFRESH, 0}, // 0 = mandatory callback - {S_ON_GETTEXT, 0}, - {S_M_EDGES, -1}, // -1 = mandatory attributes - {S_M_NODES, -1}, - {S_ON_HINT, GR_HAVE_USER_HINT}, - {S_ON_CLICK, GR_HAVE_CLICKED}, - {S_ON_DBL_CLICK, GR_HAVE_DBL_CLICKED}, - {S_ON_CLOSE, GR_HAVE_CLOSE}, - {S_ON_COMMAND, GR_HAVE_COMMAND}, - {S_ON_SELECT, GR_HAVE_CHANGED_CURRENT}, - {S_ON_ACTIVATE, GR_HAVE_GOTFOCUS}, - {S_ON_DEACTIVATE, GR_HAVE_LOSTFOCUS} - }; - cb_flags = 0; - for (int i=0;i= 0 && PyCallable_Check(attr) == 0)) - { - Py_XDECREF(attr); - return -1; - } - if (have > 0 && attr != NULL) - cb_flags |= have; - Py_XDECREF(attr); - } - - - // Keep a reference - this->self = self; - Py_INCREF(self); - - // Bind py_graph_t to python object - PyObject *py_cobj = PyCObject_FromVoidPtr(this, NULL); - PyObject_SetAttrString(self, S_M_THIS, py_cobj); - Py_DECREF(py_cobj); - - // Create form - HWND hwnd = NULL; - form = create_tform(title, &hwnd); - - // Link "form" and "py_graph" - tform_pyg.add(form, this); - - if (hwnd != NULL) - { - // get a unique graph id - netnode id; - id.create(); - gv = create_graph_viewer(form, id, s_callback, this, 0); - open_tform(form, FORM_MDI|FORM_TAB|FORM_MENU); - if (gv != NULL) - viewer_fit_window(gv); - } - else - { - show(); - } - viewer_fit_window(gv); - return 0; - } - - Py_ssize_t add_command(const char *title, const char *hotkey) - { - if ( (cb_flags & GR_HAVE_COMMAND) == 0 || gv == NULL) - return 0; - Py_ssize_t cmd_id = cmdid_pyg.id(); - bool ok = viewer_add_menu_item(gv, title, s_menucb, (void *)cmd_id, hotkey, 0); - if (!ok) - return 0; - cmdid_pyg.add(this); - return cmd_id; - } - - public: - py_graph_t() - { - form = NULL; - gv = NULL; - refresh_needed = true; - self = NULL; - } - - static void SelectNode(PyObject *self, int nid) - { - py_graph_t *_this = extract_this(self); - if (_this == NULL || _this->form == NULL) - return; - _this->jump_to_node(0); - } - - static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey) - { - py_graph_t *_this = extract_this(self); - if (_this == NULL || _this->form == NULL) - return 0; - return _this->add_command(title, hotkey); - } - - static void Close(PyObject *self) - { - py_graph_t *_this = extract_this(self); - if (_this == NULL || _this->form == NULL) - return; - close_tform(_this->form, 0); - } - - static void Refresh(PyObject *self) - { - py_graph_t *_this = extract_this(self); - if (_this == NULL) - return; - _this->refresh(); - } - - static py_graph_t *Show(PyObject *self) - { - py_graph_t *ret = extract_this(self); - if (ret == NULL) - { - qstring title; - if (!extract_title(self, &title)) - return NULL; - - // Form already created? try to get associated py_graph instance - // so that we reuse it - ret = tform_pyg.get(find_tform(title.c_str())); - - // Instance not found? create a new one - if (ret == NULL) - ret = new py_graph_t(); - else - { - // unbind so we are rebound - ret->unbind(); - ret->refresh_needed = true; - } - if (ret->create(self, title.c_str()) < 0) - { - delete ret; - ret = NULL; - } - } - else - { - ret->show(); - } - return ret; - } -}; - -py_graph_t::tform_pygraph_map_t py_graph_t::tform_pyg; -py_graph_t::cmdid_map_t py_graph_t::cmdid_pyg; - -bool pyg_show(PyObject *self) -{ - return py_graph_t::Show(self) != NULL; -} - -void pyg_refresh(PyObject *self) -{ - py_graph_t::Refresh(self); -} - -void pyg_close(PyObject *self) -{ - py_graph_t::Close(self); -} - -PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey) -{ - return Py_BuildValue("n", py_graph_t::AddCommand(self, title, hotkey)); -} - -void pyg_select_node(PyObject *self, int nid) -{ - py_graph_t::SelectNode(self, nid); -} -// -%} - -%pythoncode %{ -# -class GraphViewer: - """This class wraps the user graphing facility provided by the graph.hpp file""" - def __init__(self, title, close_open = False): - """ - Constructs the GraphView object. - Please do not remove or rename the private fields - - @param title: The title of the graph window - @param close_open: Should it attempt to close an existing graph (with same title) before creating this graph? - """ - self._title = title - self._nodes = [] - self._edges = [] - self._close_open = close_open - - def AddNode(self, obj): - """Creates a node associated with the given object and returns the node id""" - id = len(self._nodes) - self._nodes.append(obj) - return id - - def AddEdge(self, src_node, dest_node): - """Creates an edge between two given node ids""" - self._edges.append( (src_node, dest_node) ) - - def Clear(self): - """Clears all the nodes and edges""" - self._nodes = [] - self._edges = [] - - 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] - - def Count(self): - """Returns the node count""" - return len(self._nodes) - - def Close(self): - """ - Closes the graph. - It is possible to call Show() again (which will recreate the graph) - """ - _idaapi.pyg_close(self) - - def Refresh(self): - """ - Refreshes the graph. This causes the OnRefresh() to be called - """ - _idaapi.pyg_refresh(self) - - def Show(self): - """ - Shows an existing graph or creates a new one - - @return: Boolean - """ - if self._close_open: - frm = _idaapi.find_tform(self._title) - if frm: - _idaapi.close_tform(frm, 0) - return _idaapi.pyg_show(self) - - def Select(self, node_id): - """Selects a node on the graph""" - _idaapi.pyg_select_node(self, node_id) - - def AddCommand(self, title, hotkey): - """ - Adds a menu command to the graph. Call this command after the graph is shown (with Show()). - Once a command is added, a command id is returned. The commands are handled inside the OnCommand() handler - - @return: 0 on failure or the command id - """ - return _idaapi.pyg_add_command(self, title, hotkey) - - def OnRefresh(self): - """ - Event called when the graph is refreshed or first created. - From this event you are supposed to create nodes and edges. - This callback is mandatory. - @note: ***It is important to clear previous nodes before adding nodes.*** - @return: Returning true tells the graph viewer to use the items. Otherwise old items will be used. - """ - self.Clear() - - return True - -# def OnActivate(self): -# """ -# Triggered when the graph window gets the focus -# @return: None -# """ -# print "Activated...." -# -# def OnDeactivate(self): -# """Triggered when the graph window loses the focus -# @return: None -# """ -# print "Deactivated...." -# -# def OnSelect(self, node_id): -# """ -# Triggered when a node is being selected -# @return: Return True to allow the node to be selected or False to disallow node selection change -# """ -# # allow selection change -# return True - -# def OnGetText(self, node_id): -# """ -# Triggered when the graph viewer wants the text and color for a given node. -# This callback is triggered one time for a given node (the value will be cached and used later without calling Python). -# When you call refresh then again this callback will be called for each node. -# -# @return: Return a string to describe the node text or return a tuple (node_text, node_color) to describe both text and color -# """ -# return str(self[node_id]) - -# def OnHint(self, node_id): -# """ -# Triggered when the graph viewer wants to retrieve hint text associated with a given node -# -# @return: None if no hint is avail or a string designating the hint -# """ -# return "hint for " + str(node_id) -# -# def OnClose(self): -# """Triggered when the graph viewer window is being closed -# @return: None -# """ -# print "Closing......." -# -# def OnClick(self, node_id): -# """ -# Triggered when a node is clicked -# @return: False to ignore the click and true otherwise -# """ -# print "clicked on", self[node_id] -# return True -# -# def OnDblClick(self, node_id): -# """ -# Triggerd when a node is double-clicked. -# @return: False to ignore the click and True otherwise -# """ -# print "dblclicked on", self[node_id] -# return True -# -# def OnCommand(self, cmd_id): -# """ -# Triggered when a menu command is selected through the menu or its hotkey -# @return: None -# """ -# print "command:", cmd_id -# -%} - -%inline %{ -// -void pyg_refresh(PyObject *self); -void pyg_close(PyObject *self); - -PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey); -void pyg_select_node(PyObject *self, int nid); -bool pyg_show(PyObject *self); -// -%} +#ifdef __NT__ +%{ +// +class py_graph_t +{ +private: + enum + { + GR_HAVE_USER_HINT = 0x00000001, + GR_HAVE_CLICKED = 0x00000002, + GR_HAVE_DBL_CLICKED = 0x00000004, + GR_HAVE_GOTFOCUS = 0x00000008, + GR_HAVE_LOSTFOCUS = 0x00000010, + GR_HAVE_CHANGED_CURRENT = 0x00000020, + GR_HAVE_CLOSE = 0x00000040, + GR_HAVE_COMMAND = 0x00000080 + }; + struct nodetext_cache_t + { + qstring text; + bgcolor_t bgcolor; + nodetext_cache_t(const nodetext_cache_t &rhs): text(rhs.text), bgcolor(rhs.bgcolor) { } + 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: + nodetext_cache_t *get(int node_id) + { + iterator it = find(node_id); + if ( it == end() ) + return NULL; + return &it->second; + } + 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); + } + }; + + class tform_pygraph_map_t: public std::map + { + public: + py_graph_t *get(TForm *form) + { + iterator it = find(form); + return it == end() ? NULL : it->second; + } + void add(TForm *form, py_graph_t *py) + { + (*this)[form] = py; + } + }; + class cmdid_map_t: public std::map + { + private: + Py_ssize_t uid; + public: + + cmdid_map_t() + { + uid = 1; // we start by one and keep zero for error id + } + + void add(py_graph_t *pyg) + { + (*this)[uid] = pyg; + ++uid; + } + + const Py_ssize_t id() const { return uid; } + + void clear(py_graph_t *pyg) + { + iterator e = end(); + for (iterator it=begin();it!=end();) + { + if ( it->second == pyg ) + { + iterator temp = it++; + erase(temp); + } + else + ++it; + } + } + py_graph_t *get(Py_ssize_t id) + { + iterator it = find(id); + return it == end() ? NULL : it->second; + } + }; + + static tform_pygraph_map_t tform_pyg; + static cmdid_map_t cmdid_pyg; + int cb_flags; + TForm *form; + graph_viewer_t *gv; + bool refresh_needed; + PyObject *self; + nodetext_cache_map_t node_cache; + + // static callback + static int idaapi s_callback(void *obj, int code, va_list va) + { + return ((py_graph_t *)obj)->gr_callback(code, va); + } + + static bool idaapi s_menucb(void *ud) + { + Py_ssize_t id = (Py_ssize_t)ud; + 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 + PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_COMMAND, "n", id); + Py_XDECREF(ret); + } + + // Refresh user-defined graph node number and edges + // It calls Python method and expects that the user already filled + // the nodes and edges. The nodes and edges are retrieved and passed to IDA + void on_user_refresh(mutable_graph_t *g) + { + if ( !refresh_needed ) + return; + + // Check return value to OnRefresh() call + PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_REFRESH, NULL); + if ( ret == NULL || !PyObject_IsTrue(ret) ) + { + Py_XDECREF(ret); + return; + } + + // Refer to the nodes + PyObject *nodes = PyObject_TryGetAttrString(self, S_M_NODES); + if ( ret == NULL || !PyList_Check(nodes) ) + { + Py_XDECREF(nodes); + return; + } + + // Refer to the edges + PyObject *edges = PyObject_TryGetAttrString(self, S_M_EDGES); + if ( ret == NULL || !PyList_Check(nodes) ) + { + Py_DECREF(nodes); + Py_XDECREF(edges); + return; + } + + // Resize the nodes + int max_nodes = abs(int(PyList_Size(nodes))); + g->clear(); + g->resize(max_nodes); + + // Mark that we refreshed already + refresh_needed = false; + + // Clear cached nodes + node_cache.clear(); + + // Get the edges + for ( int i=(int)PyList_Size(edges)-1; i>=0; i-- ) + { + // Each list item is a sequence (id1, id2) + PyObject *item = PyList_GetItem(edges, i); + if ( !PySequence_Check(item) ) + continue; + + // Get and validate each of the two elements in the sequence + int edge_ids[2]; + int j; + for ( j=0; j 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); + } + Py_DECREF(nodes); + Py_DECREF(edges); + } + + // Retrieves the text for user-defined graph node + // It expects either a string or a tuple (string, bgcolor) + bool on_user_text(mutable_graph_t * /*g*/, int node, const char **str, bgcolor_t *bg_color) + { + // If already cached then return the value + nodetext_cache_t *c = node_cache.get(node); + if ( c != NULL ) + { + *str = c->text.c_str(); + if ( bg_color != NULL ) + *bg_color = c->bgcolor; + return true; + } + + // Not cached, call Python + PyObject *result = PyObject_CallMethod(self, (char *)S_ON_GETTEXT, "i", node); + if ( result == NULL ) + return false; + + bgcolor_t cl = bg_color == NULL ? DEFCOLOR : *bg_color; + const char *s; + + // User returned a string? + if ( PyString_Check(result) ) + { + s = PyString_AsString(result); + if ( s == NULL ) + s = ""; + c = node_cache.add(node, s, cl); + } + // User returned a sequence of text and bgcolor + else if ( PySequence_Check(result) && PySequence_Size(result) == 2 ) + { + PyObject *py_str = PySequence_GetItem(result, 0); + PyObject *py_color = PySequence_GetItem(result, 1); + + if ( py_str == NULL || !PyString_Check(py_str) || (s = PyString_AsString(py_str)) == NULL ) + s = ""; + if ( py_color != NULL && PyNumber_Check(py_color) ) + cl = bgcolor_t(PyLong_AsUnsignedLong(py_color)); + + c = node_cache.add(node, s, cl); + + Py_XDECREF(py_str); + Py_XDECREF(py_color); + } + Py_DECREF(result); + + *str = c->text.c_str(); + if ( bg_color != NULL ) + *bg_color = c->bgcolor; + return true; + } + + // Retrieves the hint for the user-defined graph + // Calls Python and expects a string or None + int on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_src*/, int /*mouseedge_dst*/, char **hint) + { + // 'hint' must be allocated by qalloc() or qstrdup() + // out: 0-use default hint, 1-use proposed hint + + // We dispatch hints over nodes only + if ( mousenode == -1 ) + return 0; + + PyObject *result = PyObject_CallMethod(self, (char *)S_ON_HINT, "i", mousenode); + bool ok = result != NULL && PyString_Check(result); + if ( !ok ) + { + Py_XDECREF(result); + return 0; + } + *hint = qstrdup(PyString_AsString(result)); + Py_DECREF(result); + return 1; // use our hint + } + + // graph is being destroyed + void on_destroy(mutable_graph_t * /*g*/ = NULL) + { + if ( self != NULL ) + { + if ( cb_flags & GR_HAVE_CLOSE ) + { + PyObject *result = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL); + Py_XDECREF(result); + } + unbind(); + } + // Remove the TForm from list + if ( form != NULL ) + tform_pyg.erase(form); + // 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) + { + // in: graph_viewer_t *gv + // selection_item_t *current_item1 + // graph_item_t *current_item2 + // out: 0-ok, 1-ignore click + // this callback allows you to ignore some clicks. + // it occurs too early, internal graph variables are not updated yet + // current_item1, current_item2 point to the same thing + // item2 has more information. + // see also: kernwin.hpp, custom_viewer_click_t + if ( item2->n == -1 ) + return 1; + + PyObject *result = PyObject_CallMethod(self, (char *)S_ON_CLICK, "i", item2->n); + if ( result == NULL || !PyObject_IsTrue(result) ) + { + Py_XDECREF(result); + return 1; + } + + Py_DECREF(result); + + return 0; + } + + // a graph node has been double clicked + int on_dblclicked(graph_viewer_t * /*gv*/, selection_item_t *item) + { + // in: graph_viewer_t *gv + // selection_item_t *current_item + // out: 0-ok, 1-ignore click + //graph_viewer_t *v = va_arg(va, graph_viewer_t *); + //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); + if ( result == NULL || !PyObject_IsTrue(result) ) + { + Py_XDECREF(result); + return 1; + } + Py_DECREF(result); + return 0; + } + + // a graph viewer got focus + void on_gotfocus(graph_viewer_t * /*gv*/) + { + PyObject *result = PyObject_CallMethod(self, (char *)S_ON_ACTIVATE, NULL); + 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); + Py_XDECREF(result); + } + + // a new graph node became the current node + int on_changed_current(graph_viewer_t * /*gv*/, int curnode) + { + // in: graph_viewer_t *gv + // int curnode + // 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); + bool allow = (result != NULL && PyObject_IsTrue(result)); + Py_XDECREF(result); + return allow ? 0 : 1; + } + + int gr_callback(int code, va_list va) + { + int ret; + switch ( code ) + { + // + case grcode_user_text: + { + mutable_graph_t *g = va_arg(va, mutable_graph_t *); + int node = va_arg(va, int); + const char **result = va_arg(va, const char **); + bgcolor_t *bgcolor = va_arg(va, bgcolor_t *); + ret = on_user_text(g, node, result, bgcolor); + break; + } + // + case grcode_destroyed: + on_destroy(va_arg(va, mutable_graph_t *)); + ret = 0; + break; + // + case grcode_clicked: + if ( cb_flags & GR_HAVE_CLICKED ) + { + graph_viewer_t *gv = va_arg(va, graph_viewer_t *); + selection_item_t *item = va_arg(va, selection_item_t *); + graph_item_t *gitem = va_arg(va, graph_item_t *); + ret = on_clicked(gv, item, gitem); + } + else + ret = 1; // ignore click + break; + // + case grcode_dblclicked: + if ( cb_flags & GR_HAVE_DBL_CLICKED ) + { + graph_viewer_t *gv = va_arg(va, graph_viewer_t *); + selection_item_t *item = va_arg(va, selection_item_t *); + ret = on_dblclicked(gv, item); + } + else + ret = 1; // ignore + break; + // + 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; + // + case grcode_user_hint: + if ( cb_flags & GR_HAVE_USER_HINT ) + { + mutable_graph_t *g = va_arg(va, mutable_graph_t *); + int mousenode = va_arg(va, int); + int mouseedge_src = va_arg(va, int); + int mouseedge_dest = va_arg(va, int); + char **hint = va_arg(va, char **); + ret = on_user_hint(g, mousenode, mouseedge_src, mouseedge_dest, hint); + } + else + { + ret = 0; + } + break; + // + case grcode_changed_current: + if ( cb_flags & GR_HAVE_CHANGED_CURRENT ) + { + graph_viewer_t *gv = va_arg(va, graph_viewer_t *); + int cur_node = va_arg(va, int); + ret = on_changed_current(gv, cur_node); + } + else + ret = 0; // allow selection change + break; + // + default: + ret = 0; + break; + } + //grcode_changed_graph, // new graph has been set + //grcode_creating_group, // a group is being created + //grcode_deleting_group, // a group is being deleted + //grcode_group_visibility, // a group is being collapsed/uncollapsed + //grcode_user_size, // calculate node size for user-defined graph + //grcode_user_title, // render node title of a user-defined graph + //grcode_user_draw, // render node of a user-defined graph + return ret; + } + + void unbind() + { + if ( self == NULL ) + return; + + // Unbind this object from the python object + PyObject *py_cobj = PyCObject_FromVoidPtr(NULL, NULL); + PyObject_SetAttrString(self, S_M_THIS, py_cobj); + Py_DECREF(py_cobj); + Py_XDECREF(self); + self = NULL; + } + + void show() + { + open_tform(form, FORM_MDI|FORM_TAB|FORM_MENU); + } + + static py_graph_t *extract_this(PyObject *self) + { + // Try to extract "this" from the python object + PyObject *py_this = PyObject_TryGetAttrString(self, S_M_THIS); + if ( py_this == NULL || !PyCObject_Check(py_this) ) + { + Py_XDECREF(py_this); + return NULL; + } + py_graph_t *ret = (py_graph_t *) PyCObject_AsVoidPtr(py_this); + Py_DECREF(py_this); + return ret; + } + + void jump_to_node(int nid) + { + viewer_center_on(gv, nid); + int x, y; + + // will return a place only when a node was previously selected + place_t *old_pl = get_custom_viewer_place(gv, false, &x, &y); + if ( old_pl != NULL ) + { + user_graph_place_t *new_pl = (user_graph_place_t *) old_pl->clone(); + new_pl->node = nid; + jumpto(gv, new_pl, x, y); + delete new_pl; + } + } + + void refresh() + { + refresh_needed = true; + refresh_viewer(gv); + } + + static bool extract_title(PyObject *self, qstring *title) + { + PyObject *py_title = PyObject_TryGetAttrString(self, S_M_TITLE); + if ( py_title == NULL ) + return false; + *title = PyString_AsString(py_title); + Py_DECREF(py_title); + return true; + } + + int create(PyObject *self, const char *title) + { + // check what callbacks we have + static const struct + { + const char *name; + int have; + } callbacks[] = + { + {S_ON_REFRESH, 0}, // 0 = mandatory callback + {S_ON_GETTEXT, 0}, + {S_M_EDGES, -1}, // -1 = mandatory attributes + {S_M_NODES, -1}, + {S_ON_HINT, GR_HAVE_USER_HINT}, + {S_ON_CLICK, GR_HAVE_CLICKED}, + {S_ON_DBL_CLICK, GR_HAVE_DBL_CLICKED}, + {S_ON_CLOSE, GR_HAVE_CLOSE}, + {S_ON_COMMAND, GR_HAVE_COMMAND}, + {S_ON_SELECT, GR_HAVE_CHANGED_CURRENT}, + {S_ON_ACTIVATE, GR_HAVE_GOTFOCUS}, + {S_ON_DEACTIVATE, GR_HAVE_LOSTFOCUS} + }; + cb_flags = 0; + for ( int i=0; i= 0 && PyCallable_Check(attr) == 0)) + { + Py_XDECREF(attr); + return -1; + } + if ( have > 0 && attr != NULL ) + cb_flags |= have; + Py_XDECREF(attr); + } + + + // Keep a reference + this->self = self; + Py_INCREF(self); + + // Bind py_graph_t to python object + PyObject *py_cobj = PyCObject_FromVoidPtr(this, NULL); + PyObject_SetAttrString(self, S_M_THIS, py_cobj); + Py_DECREF(py_cobj); + + // Create form + HWND hwnd = NULL; + form = create_tform(title, &hwnd); + + // Link "form" and "py_graph" + tform_pyg.add(form, this); + + if ( hwnd != NULL ) + { + // get a unique graph id + netnode id; + id.create(); + gv = create_graph_viewer(form, id, s_callback, this, 0); + open_tform(form, FORM_MDI|FORM_TAB|FORM_MENU); + if ( gv != NULL ) + viewer_fit_window(gv); + } + else + { + show(); + } + viewer_fit_window(gv); + return 0; + } + + Py_ssize_t add_command(const char *title, const char *hotkey) + { + if ( (cb_flags & GR_HAVE_COMMAND) == 0 || gv == NULL) + return 0; + Py_ssize_t cmd_id = cmdid_pyg.id(); + bool ok = viewer_add_menu_item(gv, title, s_menucb, (void *)cmd_id, hotkey, 0); + if ( !ok ) + return 0; + cmdid_pyg.add(this); + return cmd_id; + } + + public: + py_graph_t() + { + form = NULL; + gv = NULL; + refresh_needed = true; + self = NULL; + } + + static void SelectNode(PyObject *self, int nid) + { + py_graph_t *_this = extract_this(self); + if ( _this == NULL || _this->form == NULL ) + return; + _this->jump_to_node(0); + } + + static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey) + { + py_graph_t *_this = extract_this(self); + if ( _this == NULL || _this->form == NULL ) + return 0; + return _this->add_command(title, hotkey); + } + + static void Close(PyObject *self) + { + py_graph_t *_this = extract_this(self); + if ( _this == NULL || _this->form == NULL ) + return; + close_tform(_this->form, 0); + } + + static void Refresh(PyObject *self) + { + py_graph_t *_this = extract_this(self); + if ( _this == NULL ) + return; + _this->refresh(); + } + + static py_graph_t *Show(PyObject *self) + { + py_graph_t *ret = extract_this(self); + if ( ret == NULL ) + { + qstring title; + if ( !extract_title(self, &title) ) + return NULL; + + // Form already created? try to get associated py_graph instance + // so that we reuse it + ret = tform_pyg.get(find_tform(title.c_str())); + + // Instance not found? create a new one + if ( ret == NULL ) + ret = new py_graph_t(); + else + { + // unbind so we are rebound + ret->unbind(); + ret->refresh_needed = true; + } + if ( ret->create(self, title.c_str()) < 0 ) + { + delete ret; + ret = NULL; + } + } + else + { + ret->show(); + } + return ret; + } +}; + +py_graph_t::tform_pygraph_map_t py_graph_t::tform_pyg; +py_graph_t::cmdid_map_t py_graph_t::cmdid_pyg; + +bool pyg_show(PyObject *self) +{ + return py_graph_t::Show(self) != NULL; +} + +void pyg_refresh(PyObject *self) +{ + py_graph_t::Refresh(self); +} + +void pyg_close(PyObject *self) +{ + py_graph_t::Close(self); +} + +PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey) +{ + return Py_BuildValue("n", py_graph_t::AddCommand(self, title, hotkey)); +} + +void pyg_select_node(PyObject *self, int nid) +{ + py_graph_t::SelectNode(self, nid); +} +// +%} +#endif + +#ifdef __NT__ +%inline %{ +// +void pyg_refresh(PyObject *self); +void pyg_close(PyObject *self); + +PyObject *pyg_add_command(PyObject *self, const char *title, const char *hotkey); +void pyg_select_node(PyObject *self, int nid); +bool pyg_show(PyObject *self); +// +%} +#endif + +#ifdef __NT__ +%pythoncode %{ +# +class GraphViewer: + """This class wraps the user graphing facility provided by the graph.hpp file""" + def __init__(self, title, close_open = False): + """ + Constructs the GraphView object. + Please do not remove or rename the private fields + + @param title: The title of the graph window + @param close_open: Should it attempt to close an existing graph (with same title) before creating this graph? + """ + self._title = title + self._nodes = [] + self._edges = [] + self._close_open = close_open + + def AddNode(self, obj): + """Creates a node associated with the given object and returns the node id""" + id = len(self._nodes) + self._nodes.append(obj) + return id + + def AddEdge(self, src_node, dest_node): + """Creates an edge between two given node ids""" + self._edges.append( (src_node, dest_node) ) + + def Clear(self): + """Clears all the nodes and edges""" + self._nodes = [] + self._edges = [] + + 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] + + def Count(self): + """Returns the node count""" + return len(self._nodes) + + def Close(self): + """ + Closes the graph. + It is possible to call Show() again (which will recreate the graph) + """ + _idaapi.pyg_close(self) + + def Refresh(self): + """ + Refreshes the graph. This causes the OnRefresh() to be called + """ + _idaapi.pyg_refresh(self) + + def Show(self): + """ + Shows an existing graph or creates a new one + + @return: Boolean + """ + if self._close_open: + frm = _idaapi.find_tform(self._title) + if frm: + _idaapi.close_tform(frm, 0) + return _idaapi.pyg_show(self) + + def Select(self, node_id): + """Selects a node on the graph""" + _idaapi.pyg_select_node(self, node_id) + + def AddCommand(self, title, hotkey): + """ + Adds a menu command to the graph. Call this command after the graph is shown (with Show()). + Once a command is added, a command id is returned. The commands are handled inside the OnCommand() handler + + @return: 0 on failure or the command id + """ + return _idaapi.pyg_add_command(self, title, hotkey) + + def OnRefresh(self): + """ + Event called when the graph is refreshed or first created. + From this event you are supposed to create nodes and edges. + This callback is mandatory. + + @note: ***It is important to clear previous nodes before adding nodes.*** + @return: Returning True tells the graph viewer to use the items. Otherwise old items will be used. + """ + self.Clear() + + return True +# +# def OnGetText(self, node_id): +# """ +# Triggered when the graph viewer wants the text and color for a given node. +# This callback is triggered one time for a given node (the value will be cached and used later without calling Python). +# When you call refresh then again this callback will be called for each node. +# +# This callback is mandatory. +# +# @return: Return a string to describe the node text or return a tuple (node_text, node_color) to describe both text and color +# """ +# return str(self[node_id]) +# +# def OnActivate(self): +# """ +# Triggered when the graph window gets the focus +# @return: None +# """ +# print "Activated...." +# +# def OnDeactivate(self): +# """Triggered when the graph window loses the focus +# @return: None +# """ +# print "Deactivated...." +# +# def OnSelect(self, node_id): +# """ +# Triggered when a node is being selected +# @return: Return True to allow the node to be selected or False to disallow node selection change +# """ +# # allow selection change +# return True +# +# def OnHint(self, node_id): +# """ +# Triggered when the graph viewer wants to retrieve hint text associated with a given node +# +# @return: None if no hint is avail or a string designating the hint +# """ +# return "hint for " + str(node_id) +# +# def OnClose(self): +# """Triggered when the graph viewer window is being closed +# @return: None +# """ +# print "Closing......." +# +# def OnClick(self, node_id): +# """ +# Triggered when a node is clicked +# @return: False to ignore the click and True otherwise +# """ +# print "clicked on", self[node_id] +# return True +# +# def OnDblClick(self, node_id): +# """ +# Triggerd when a node is double-clicked. +# @return: False to ignore the click and True otherwise +# """ +# print "dblclicked on", self[node_id] +# return True +# +# def OnCommand(self, cmd_id): +# """ +# Triggered when a menu command is selected through the menu or its hotkey +# @return: None +# """ +# print "command:", cmd_id +# +# +%} +#endif diff --git a/swig/ida.i b/swig/ida.i index 77709eb..7c6e417 100644 --- a/swig/ida.i +++ b/swig/ida.i @@ -1,23 +1,23 @@ -// Ignore kernel-only symbols -%ignore dual_text_options_t; -%ignore idainfo::init; -%ignore idainfo::retrieve; -%ignore idainfo::read; -%ignore idainfo::write; -%ignore idainfo::align_short_demnames; -%ignore idainfo::align_strtype; -%ignore idainfo::align_long_demnames; - -%ignore setflag(uchar &where,uchar bit,int value); -%ignore setflag(ushort &where,ushort bit,int value); -%ignore setflag(uint32 &where,uint32 bit,int value); - -// Make idainfo::get_proc_name() work -%cstring_bounded_output(char *buf, 8); - -%ignore BADADDR; -%ignore BADSEL; - -%include "ida.hpp" - -%clear(char *buf); +// Ignore kernel-only symbols +%ignore dual_text_options_t; +%ignore idainfo::init; +%ignore idainfo::retrieve; +%ignore idainfo::read; +%ignore idainfo::write; +%ignore idainfo::align_short_demnames; +%ignore idainfo::align_strtype; +%ignore idainfo::align_long_demnames; + +%ignore setflag(uchar &where,uchar bit,int value); +%ignore setflag(ushort &where,ushort bit,int value); +%ignore setflag(uint32 &where,uint32 bit,int value); + +// Make idainfo::get_proc_name() work +%cstring_bounded_output(char *buf, 8); + +%ignore BADADDR; +%ignore BADSEL; + +%include "ida.hpp" + +%clear(char *buf); diff --git a/swig/idaapi.i b/swig/idaapi.i index 08aed34..10ee505 100644 --- a/swig/idaapi.i +++ b/swig/idaapi.i @@ -1,1116 +1,2114 @@ -%module(docstring="IDA Pro Plugin SDK API wrapper",directors="1") idaapi -// Suppress 'previous definition of XX' warnings -#pragma SWIG nowarn=302 -// Enable automatic docstring generation -%feature(autodoc); -%{ -#include -#define USE_DANGEROUS_FUNCTIONS 1 -#ifdef HAVE_SSIZE_T -#define _SSIZE_T_DEFINED 1 -#endif -#if defined(__NT__) && !defined(_WINDOWS_) - #define _WINDOWS_ // kernwin.hpp needs it to declare create_tform() - typedef void *HWND; // we don't need to include windows.h for just this definition -#endif -#include "ida.hpp" -#include "idp.hpp" -#include "allins.hpp" -#include "auto.hpp" -#include "bytes.hpp" -#include "dbg.hpp" -#include "diskio.hpp" -#include "entry.hpp" -#include "enum.hpp" -#include "expr.hpp" -#include "frame.hpp" -#include "fixup.hpp" -#include "funcs.hpp" -#include "gdl.hpp" -#include "idd.hpp" -#include "ints.hpp" -#include "kernwin.hpp" -#include "lines.hpp" -#include "loader.hpp" -#include "moves.hpp" -#include "netnode.hpp" -#include "nalt.hpp" -#include "name.hpp" -#include "offset.hpp" -#include "queue.hpp" -#include "search.hpp" -#include "srarea.hpp" -#include "strlist.hpp" -#include "struct.hpp" -#include "typeinf.hpp" -#include "ua.hpp" -#include "xref.hpp" -#include "ieee.h" -#include "err.h" -#include "fpro.h" -#include -#ifdef __NT__ - #include "graph.hpp" -#endif - -// - -#include "pywraps.hpp" - -//------------------------------------------------------------------------ -// String constants used -static const char PY_IDC_CLASS_NAME[] = "py_idc_object_class"; -static const char PY_IDC_GLOBAL_VAR_FMT[] = "__py_cvt_gvar_%d"; -static const char PY_IDCCVT_ID_ATTR[] = "__idc_cvt_id__"; -static const char PY_IDCCVT_VALUE_ATTR[] = "__idc_cvt_value__"; -static const char S_PY_IDC_OPAQUE_T[] = "py_idc_cvt_helper_t"; -static const char S_PROPS[] = "props"; -static const char S_NAME[] = "name"; -static const char S_ASM_KEYWORD[] = "asm_keyword"; -static const char S_MENU_NAME[] = "menu_name"; -static const char S_HOTKEY[] = "hotkey"; -static const char S_VALUE_SIZE[] = "value_size"; -static const char S_MAY_CREATE_AT[] = "may_create_at"; -static const char S_CALC_ITEM_SIZE[] = "calc_item_size"; -static const char S_ID[] = "id"; -static const char S_PRINTF[] = "printf"; -static const char S_TEXT_WIDTH[] = "text_width"; -static const char S_SCAN[] = "scan"; -static const char S_ANALYZE[] = "analyze"; -static const char S_CBSIZE[] = "cbsize"; -static const char S_ON_CLICK[] = "OnClick"; -static const char S_ON_CLOSE[] = "OnClose"; -static const char S_ON_DBL_CLICK[] = "OnDblClick"; -static const char S_ON_CURSOR_POS_CHANGED[] = "OnCursorPosChanged"; -static const char S_ON_KEYDOWN[] = "OnKeydown"; -static const char S_ON_POPUP[] = "OnPopup"; -static const char S_ON_HINT[] = "OnHint"; -static const char S_ON_POPUP_MENU[] = "OnPopupMenu"; -static const char S_ON_EDIT_LINE[] = "OnEditLine"; -static const char S_ON_INSERT_LINE[] = "OnInsertLine"; -static const char S_ON_GET_LINE[] = "OnGetLine"; -static const char S_ON_DELETE_LINE[] = "OnDeleteLine"; -static const char S_ON_REFRESH[] = "OnRefresh"; -static const char S_ON_SELECT_LINE[] = "OnSelectLine"; -static const char S_ON_COMMAND[] = "OnCommand"; -static const char S_ON_GET_ICON[] = "OnGetIcon"; -static const char S_ON_GET_LINE_ATTR[] = "OnGetLineAttr"; -static const char S_ON_GET_SIZE[] = "OnGetSize"; -static const char S_ON_GETTEXT[] = "OnGetText"; -static const char S_ON_ACTIVATE[] = "OnActivate"; -static const char S_ON_DEACTIVATE[] = "OnDeactivate"; -static const char S_ON_SELECT[] = "OnSelect"; -static const char S_M_EDGES[] = "_edges"; -static const char S_M_NODES[] = "_nodes"; -static const char S_M_THIS[] = "_this"; -static const char S_M_TITLE[] = "_title"; - -#ifdef __PYWRAPS__ -static const char S_PY_IDAAPI_MODNAME[] = "__main__"; -#else -static const char S_PY_IDAAPI_MODNAME[] = "idaapi"; -#endif - -//------------------------------------------------------------------------ -// Constants used by get_idaapi_class_reference() -#define PY_CLSID_CVT_INT64 0 -#define PY_CLSID_APPCALL_SKEL_OBJ 1 -#define PY_CLSID_CVT_BYREF 2 -#define PY_CLSID_LAST 3 - -//------------------------------------------------------------------------ -static PyObject *py_cvt_helper_module = NULL; -static bool pywraps_initialized = false; - -//------------------------------------------------------------------------ -static idc_class_t *get_py_idc_cvt_opaque() -{ - return find_idc_class(S_PY_IDC_OPAQUE_T); -} - -//------------------------------------------------------------------------ -// IDC Opaque object destructor: when the IDC object dies we kill the -// opaque Python object along with it -static const char py_idc_cvt_helper_dtor_args[] = { VT_OBJ, 0 }; -static error_t idaapi py_idc_opaque_dtor( - idc_value_t *argv, - idc_value_t *res) -{ - // Get the value from the object - idc_value_t idc_val; - VarGetAttr(&argv[0], PY_IDCCVT_VALUE_ATTR, &idc_val); - - // Extract the Python object reference - PyObject *py_obj = (PyObject *)idc_val.pvoid; - // Decrease its reference (and eventually destroy it) - Py_DECREF(py_obj); - return eOk; -} - -//------------------------------------------------------------------------ -// This function must be called on initialization -bool init_pywraps() -{ - if (pywraps_initialized) - return true; - - // Take a reference to the idaapi python module - // (We need it to create instances of certain classes) - if (py_cvt_helper_module == NULL) - { - // Take a reference to the module so we can create the needed class instances - py_cvt_helper_module = PyImport_TryImportModule(S_PY_IDAAPI_MODNAME); - if (py_cvt_helper_module == NULL) - return false; - } - - if (get_py_idc_cvt_opaque() == NULL) - { - // Add the class - idc_class_t *idc_cvt_opaque = add_idc_class(S_PY_IDC_OPAQUE_T); - if (idc_cvt_opaque == NULL) - return false; - - // Form the dtor name - char dtor_name[MAXSTR]; - qsnprintf(dtor_name, sizeof(dtor_name), "%s.dtor", S_PY_IDC_OPAQUE_T); - - // register the dtor function - if (!set_idc_func(dtor_name, py_idc_opaque_dtor, py_idc_cvt_helper_dtor_args)) - return false; - - // Link the dtor function to the class - set_idc_dtor(idc_cvt_opaque, dtor_name); - } - pywraps_initialized = true; - return true; -} - -//------------------------------------------------------------------------ -// This function must be called on de-initialization -void deinit_pywraps() -{ - if (!pywraps_initialized) - return; - pywraps_initialized = false; - Py_XDECREF(py_cvt_helper_module); - py_cvt_helper_module = NULL; -} -//------------------------------------------------------------------------ -// Gets a class type reference in idaapi -// With the class type reference we can create a new instance of that type -// This function takes a reference to the idaapi module and keeps the reference -static PyObject *get_idaapi_class_reference(const int class_id) -{ - if (class_id >= PY_CLSID_LAST) - return NULL; - - // Some class names. The array is parallel with the PY_CLSID_xxx consts - static const char *class_names[]= - { - "PyIdc_cvt_int64__", - "Appcall_object__", - "PyIdc_cvt_refclass__" - }; - return PyObject_GetAttrString(py_cvt_helper_module, class_names[class_id]); -} - -//------------------------------------------------------------------------ -// Returns an attribute or NULL -// No errors will be set if the attribute did not exist -PyObject *PyObject_TryGetAttrString(PyObject *py_var, const char *attr) -{ - if (!PyObject_HasAttrString(py_var, attr)) - return NULL; - return PyObject_GetAttrString(py_var, attr); -} - -//------------------------------------------------------------------------ -// Tries to import a module and clears the exception on failure -PyObject *PyImport_TryImportModule(const char *name) -{ - PyObject *result = PyImport_ImportModule(name); - if (result != NULL) - return result; - if (PyErr_Occurred()) - PyErr_Clear(); - return NULL; -} - -//------------------------------------------------------------------------- -// Converts a Python number into an IDC value (32 or 64bits) -// The function will first try to convert the number into a 32bit value -// If the number does not fit then VT_INT64 will be used -// NB: This function cannot properly detect if the Python value should be -// converted to a VT_INT64 or not. For example: 2**32-1 = 0xffffffff which -// can fit in a C long but Python creates a PyLong object for it. -// And because of that we are confused as to whether to convert to 32 or 64 -bool PyGetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var) -{ - if (!(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var))) - return false; - - // Can we convert to C long? - long l = PyInt_AsLong(py_var); - if (!PyErr_Occurred()) - { - idc_var->set_long(l); - return true; - } - // Clear last error - PyErr_Clear(); - // Can be fit into a C unsigned long? - l = (long) PyLong_AsUnsignedLong(py_var); - if (!PyErr_Occurred()) - { - idc_var->set_long(l); - return true; - } - PyErr_Clear(); - idc_var->set_int64(PyLong_AsLongLong(py_var)); - return true; -} - -//------------------------------------------------------------------------- -// Parses a Python object as a long or long long -bool PyGetNumber(PyObject *py_var, uint64 *num, bool *is_64) -{ - if (!(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var))) - return false; - - // Can we convert to C long? - long l = PyInt_AsLong(py_var); - if (!PyErr_Occurred()) - { - if (num != NULL) - *num = uint64(l); - if (is_64 != NULL) - *is_64 = false; - return true; - } - // Clear last error - PyErr_Clear(); - // Can be fit into a C unsigned long? - unsigned long ul = PyLong_AsUnsignedLong(py_var); - if (!PyErr_Occurred()) - { - if (num != NULL) - *num = uint64(ul); - if (is_64 != NULL) - *is_64 = false; - return true; - } - PyErr_Clear(); - PY_LONG_LONG ll = PyLong_AsLongLong(py_var); - if (!PyErr_Occurred()) - { - if (num != NULL) - *num = uint64(ll); - if (is_64 != NULL) - *is_64 = true; - return true; - } - PyErr_Clear(); - return false; -} - -//------------------------------------------------------------------------- -// Checks if a given object is of sequence type -bool PyIsSequenceType(PyObject *obj) -{ - if (!PySequence_Check(obj)) - return false; - Py_ssize_t sz = PySequence_Size(obj); - if (sz == -1 || PyErr_Occurred() != NULL) - { - PyErr_Clear(); - return false; - } - return true; -} - -//------------------------------------------------------------------------- -// Returns the string representation of an object -bool PyObjectToString(PyObject *obj, qstring *out) -{ - PyObject *py_str = PyObject_Str(obj); - if (py_str != NULL) - { - *out = PyString_AsString(py_str); - Py_DECREF(py_str); - return true; - } - else - { - out->qclear(); - return false; - } -} - -//-------------------------------------------------------------------------- -// Checks if a Python error occured and fills the out parameter with the -// exception string -bool PyGetError(qstring *out) -{ - PyObject *py_err; - if ((py_err = PyErr_Occurred()) == NULL) - return false; - - PyObject *err_type, *err_value, *err_traceback; - PyErr_Fetch(&err_type, &err_value, &err_traceback); - if ( out != NULL ) - PyObjectToString(err_value, out); - return true; -} - -//------------------------------------------------------------------------- -// A loud version of PyGetError() which gets the error and displays it -bool PyShowErr(const char *cb_name) -{ - static qstring err_str; - if (!PyGetError(&err_str)) - return false; - warning("IDAPython: Error while calling Python callback <%s>:\n%s", cb_name, err_str.c_str()); - return true; -} - -//------------------------------------------------------------------------- -// Checks if the given py_var is a special PyIdc_cvt_helper object. -// It does that by examining the magic attribute and returns its numeric value. -// It returns -1 if the object is not a recognized helper object. -// Any Python object can be treated as an cvt object if this attribute is created. -static int get_pyidc_cvt_type(PyObject *py_var) -{ - // Check if this our special by reference object - PyObject *attr = PyObject_TryGetAttrString(py_var, PY_IDCCVT_ID_ATTR); - if (attr == NULL) - return -1; - if (!(PyInt_Check(attr) || PyLong_Check(attr))) - { - Py_DECREF(attr); - return -1; - } - int r = (int)PyInt_AsLong(attr); - Py_DECREF(attr); - return r; -} - -//------------------------------------------------------------------------- -// Utility function to create opaque / convertible Python <-> IDC variables -// The referred Python variable will have its reference increased -static bool create_py_idc_opaque_obj(PyObject *py_var, idc_value_t *idc_var) -{ - // Create an IDC object of this special helper class - if (VarObject(idc_var, get_py_idc_cvt_opaque()) != eOk) - return false; - - // Store the CVT id - idc_value_t idc_val; - idc_val.set_long(PY_ICID_OPAQUE); - VarSetAttr(idc_var, PY_IDCCVT_ID_ATTR, &idc_val); - - // Store the value as a PVOID referencing the given Python object - idc_val.set_pvoid(py_var); - VarSetAttr(idc_var, PY_IDCCVT_VALUE_ATTR, &idc_val); - - return true; -} - -//------------------------------------------------------------------------- -// Converts a Python variable into an IDC variable -// This function returns on one CIP_XXXX -int pyvar_to_idcvar( - PyObject *py_var, - idc_value_t *idc_var, - int *gvar_sn) -{ - PyObject *attr; - // None / NULL - if (py_var == NULL || py_var == Py_None) - idc_var->set_long(0); - // Numbers? - else if (PyGetNumberAsIDC(py_var, idc_var)) - return CIP_OK; - // String - else if (PyString_Check(py_var)) - idc_var->_set_string(PyString_AsString(py_var), PyString_Size(py_var)); - // Float - else if (PyBool_Check(py_var)) - idc_var->set_long(py_var == Py_True ? 1 : 0); - // Boolean - else if (PyFloat_Check(py_var)) - { - double dresult = PyFloat_AsDouble(py_var); - ieee_realcvt((void *)&dresult, idc_var->e, 3); - idc_var->vtype = VT_FLOAT; - } - // void* - else if (PyCObject_Check(py_var)) - idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var)); - // Is it a Python list? - else if (PyList_CheckExact(py_var) || PyIsSequenceType(py_var)) - { - // Create the object - VarObject(idc_var); - - // Determine list size and type - bool is_seq = !PyList_CheckExact(py_var); - Py_ssize_t size = is_seq ? PySequence_Size(py_var) : PyList_Size(py_var); - bool ok = true; - qstring attr_name; - - // Convert each item - for (Py_ssize_t i=0;i= CIP_OK; - if (ok) - { - // Form the attribute name - PyObject *py_int = PyInt_FromSsize_t(i); - ok = PyObjectToString(py_int, &attr_name); - if (!ok) - break; - Py_DECREF(py_int); - // Store the attribute - VarSetAttr(idc_var, attr_name.c_str(), &v); - } - // Sequences return a new reference for GetItem() - if (is_seq) - Py_DECREF(py_var); - if (!ok) - break; - } - return ok ? CIP_OK : CIP_FAILED; - } - // Dictionary: we convert to an IDC object - else if (PyDict_Check(py_var)) - { - // Create an empty IDC object - VarObject(idc_var); - - // Get the dict.items() list - PyObject *py_items = PyDict_Items(py_var); - - // Get the size of the list - qstring key_name; - bool ok = true; - Py_ssize_t size = PySequence_Size(py_items); - for (Py_ssize_t i=0;i (key, value) - PyObject *py_item = PyList_GetItem(py_items, i); - - // Extract key/value - PyObject *key = PySequence_GetItem(py_item, 0); - PyObject *val = PySequence_GetItem(py_item, 1); - - // Get key's string representation - PyObjectToString(key, &key_name); - - // Convert the attribute into an IDC value - idc_value_t v; - ok = pyvar_to_idcvar(val, &v, gvar_sn) >= CIP_OK; - if (ok) - { - // Store the attribute - VarSetAttr(idc_var, key_name.c_str(), &v); - } - Py_XDECREF(key); - Py_XDECREF(val); - if (!ok) - break; - } - // Decrement attribute reference - Py_DECREF(py_items); - return ok ? CIP_OK : CIP_FAILED; - } - // Possible function? - else if (PyCallable_Check(py_var)) - { - idc_var->clear(); - idc_var->vtype = VT_FUNC; - idc_var->funcidx = -1; // Does not apply - return CIP_OK; - } - // Objects: - // - pyidc_cvt objects: int64, byref, opaque - // - other python objects - else - { - // Get the type - int cvt_id = get_pyidc_cvt_type(py_var); - switch (cvt_id) - { - // - // INT64 - // - case PY_ICID_INT64: - // Get the value attribute - attr = PyObject_TryGetAttrString(py_var, PY_IDCCVT_VALUE_ATTR); - if (attr == NULL) - return false; - idc_var->set_int64(PyLong_AsLongLong(attr)); - Py_DECREF(attr); - return CIP_OK; - // - // BYREF - // - case PY_ICID_BYREF: - { - // BYREF always require this parameter - if (gvar_sn == NULL) - return CIP_FAILED; - - // Get the value attribute - attr = PyObject_TryGetAttrString(py_var, PY_IDCCVT_VALUE_ATTR); - if (attr == NULL) - return CIP_FAILED; - - // Create a global variable - char buf[MAXSTR]; - qsnprintf(buf, sizeof(buf), PY_IDC_GLOBAL_VAR_FMT, *gvar_sn); - idc_value_t *gvar = add_idc_gvar(buf); - // Convert the python value into the IDC global variable - bool ok = pyvar_to_idcvar(attr, gvar, gvar_sn) >= CIP_OK; - if (ok) - { - (*gvar_sn)++; - // Create a reference to this global variable - VarRef(idc_var, gvar); - } - Py_DECREF(attr); - return ok ? CIP_OK : CIP_FAILED; - } - // - // OPAQUE - // - case PY_ICID_OPAQUE: - { - if (!create_py_idc_opaque_obj(py_var, idc_var)) - return CIP_FAILED; - return CIP_OK_NODECREF; - } - // - // Other objects - // - default: - // A normal object? - PyObject *py_dir = PyObject_Dir(py_var); - Py_ssize_t size = PyList_Size(py_dir); - if (py_dir == NULL || !PyList_Check(py_dir) || size == 0) - { - Py_XDECREF(py_dir); - return CIP_FAILED; - } - // Create the IDC object - VarObject(idc_var); - for (Py_ssize_t i=0;i 2 ) - && (strncmp(field_name, "__", 2) == 0 ) - && (strncmp(field_name+len-2, "__", 2) == 0) ) - { - continue; - } - idc_value_t v; - // Get the non-private attribute from the object - attr = PyObject_GetAttrString(py_var, field_name); - if (attr == NULL - // Convert the attribute into an IDC value - || pyvar_to_idcvar(attr, &v, gvar_sn) < CIP_OK) - { - Py_XDECREF(attr); - return CIP_FAILED; - } - // Store the attribute - VarSetAttr(idc_var, field_name, &v); - // Decrement attribute reference - Py_DECREF(attr); - } - } - } - return CIP_OK; -} - -//------------------------------------------------------------------------- -// Converts an IDC variable to a Python variable -// If py_var points to an existing object then the object will be updated -// If py_var points to an existing immutable object then ZERO is returned -// Returns one of CIP_xxxx. Check pywraps.hpp -int idcvar_to_pyvar( - const idc_value_t &idc_var, - PyObject **py_var) -{ - switch (idc_var.vtype) - { - case VT_PVOID: - if (*py_var == NULL) - *py_var = PyCObject_FromVoidPtr(idc_var.pvoid, NULL); - else - return CIP_IMMUTABLE; - break; - case VT_INT64: - { - // Recycle? - if (*py_var != NULL) - { - // Recycling an int64 object? - int t = get_pyidc_cvt_type(*py_var); - if (t != PY_ICID_INT64) - return CIP_IMMUTABLE; // Cannot recycle immutable object - // Update the attribute - PyObject_SetAttrString(*py_var, PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); - return CIP_OK; - } - PyObject *py_cls = get_idaapi_class_reference(PY_CLSID_CVT_INT64); - if (py_cls == NULL) - return CIP_FAILED; - *py_var = PyObject_CallFunctionObjArgs(py_cls, PyLong_FromLongLong(idc_var.i64), NULL); - Py_DECREF(py_cls); - break; - } -#if !defined(NO_OBSOLETE_FUNCS) || defined(__EXPR_SRC) - case VT_STR: - *py_var = PyString_FromString(idc_var.str); - break; -#endif - case VT_STR2: - if (*py_var == NULL) - { - const qstring &s = idc_var.qstr(); - *py_var = PyString_FromStringAndSize(s.begin(), s.length()); - break; - } - else - return CIP_IMMUTABLE; // Cannot recycle immutable object - case VT_LONG: - // Cannot recycle immutable objects - if (*py_var != NULL) - return CIP_IMMUTABLE; -#ifdef __EA64__ - *py_var = PyLong_FromLongLong(idc_var.num); -#else - *py_var = PyLong_FromLong(idc_var.num); -#endif - break; - case VT_FLOAT: - if (*py_var == NULL) - { - double x; - if ( ph.realcvt(&x, (uint16 *)idc_var.e, (sizeof(x)/2-1)|010) != 0 ) - INTERR(); - *py_var = PyFloat_FromDouble(x); - break; - } - else - return CIP_IMMUTABLE; - case VT_REF: - { - if (*py_var == NULL) - { - PyObject *py_cls = get_idaapi_class_reference(PY_CLSID_CVT_BYREF); - if (py_cls == NULL) - return CIP_FAILED; - // Create a byref object with None value. We populate it later - *py_var = PyObject_CallFunctionObjArgs(py_cls, Py_None, NULL); - Py_DECREF(py_cls); - } - int t = *py_var == NULL ? -1 : get_pyidc_cvt_type(*py_var); - if (t != PY_ICID_BYREF) - return CIP_FAILED; - - // Dereference - // (Since we are not using VREF_COPY flag, we can safely const_cast) - idc_value_t *dref_v = VarDeref(const_cast(&idc_var), VREF_LOOP); - if (dref_v == NULL) - return CIP_FAILED; - - // Can we recycle the object? - PyObject *new_py_val = PyObject_TryGetAttrString(*py_var, PY_IDCCVT_VALUE_ATTR); - if (new_py_val != NULL) - { - // Recycle - t = idcvar_to_pyvar(*dref_v, &new_py_val); - Py_XDECREF(new_py_val); // DECREF because of GetAttrStr - // Success? Nothing more to be done - if (t == CIP_OK) - return CIP_OK; - // Clear it so we don't recycle it - new_py_val = NULL; - } - // Try to convert (not recycle) - if (idcvar_to_pyvar(*dref_v, &new_py_val) != CIP_OK) - return CIP_FAILED; - // Update the attribute - PyObject_SetAttrString(*py_var, PY_IDCCVT_VALUE_ATTR, new_py_val); - Py_DECREF(new_py_val); - break; - } - // Can convert back into a Python object or Python dictionary - // (Depending if py_var will be recycled and it was a dictionary) - case VT_OBJ: - { - // Check if this IDC object has __cvt_id__ and the __idc_cvt_value__ fields - idc_value_t idc_val; - if ( VarGetAttr(&idc_var, PY_IDCCVT_ID_ATTR, &idc_val) == eOk - && VarGetAttr(&idc_var, PY_IDCCVT_VALUE_ATTR, &idc_val) == eOk ) - { - // Extract the object - *py_var = (PyObject *) idc_val.pvoid; - return CIP_OK_NODECREF; - } - PyObject *obj; - bool is_dict = false; - // Need to create a new object? - if (*py_var == NULL) - { - PyObject *py_cls = get_idaapi_class_reference(PY_CLSID_APPCALL_SKEL_OBJ); - if (py_cls == NULL) - return CIP_FAILED; - obj = PyObject_CallFunctionObjArgs(py_cls, NULL); - Py_DECREF(py_cls); - if (obj == NULL) - return CIP_FAILED; - } - else - { - // Recycle existing variable - obj = *py_var; - if (PyDict_Check(obj)) - is_dict = true; - } - // Walk the IDC attributes and store into python - for (const char *attr_name = VarFirstAttr(&idc_var); - attr_name != NULL; - attr_name=VarNextAttr(&idc_var, attr_name)) - { - // Get the attribute - idc_value_t v; - VarGetAttr(&idc_var, attr_name, &v, true); - // Convert attribute to a python value - PyObject *py_attr(NULL); - int cvt = idcvar_to_pyvar(v, &py_attr); - if (cvt <= CIP_IMMUTABLE) - { - // Delete the object (if we created it) - if (*py_var == NULL) - Py_DECREF(obj); - return CIP_FAILED; - } - if (is_dict) - PyDict_SetItemString(obj, attr_name, py_attr); - else - PyObject_SetAttrString(obj, attr_name, py_attr); - if (cvt == CIP_OK) - Py_DECREF(py_attr); - } - *py_var = obj; - break; - } - // Unhandled type - default: - *py_var = NULL; - return CIP_FAILED; - } - return CIP_OK; -} - -// -%} - -// Do not create separate wrappers for default arguments -%feature("compactdefaultargs"); - -#ifdef __EA64__ -#ifdef __GNUC__ -%constant ea_t BADADDR = 0xFFFFFFFFFFFFFFFFll; -%constant sel_t BADSEL = 0xFFFFFFFFFFFFFFFFll; -%constant nodeidx_t BADNODE = 0xFFFFFFFFFFFFFFFFll; -#else // __GNUC__ -%constant ea_t BADADDR = 0xFFFFFFFFFFFFFFFFui64; -%constant sel_t BADSEL = 0xFFFFFFFFFFFFFFFFui64; -%constant nodeidx_t BADNODE = 0xFFFFFFFFFFFFFFFFui64; -#endif // __GNUC__ -#else //__EA64__ -%constant ea_t BADADDR = 0xFFFFFFFFL; -%constant sel_t BADSEL = 0xFFFFFFFFL; -%constant nodeidx_t BADNODE = 0xFFFFFFFFL; -#endif - -// Help SWIG to figure out the ulonglong type -#ifdef SWIGWIN -typedef unsigned __int64 ulonglong; -typedef __int64 longlong; -#else -typedef unsigned long long ulonglong; -typedef long long longlong; -#endif - -typedef int error_t; - -%include "typemaps.i" - -%include "cstring.i" -%include "carrays.i" -%include "cpointer.i" - -%include "typeconv.i" - -%include "pro.h" - -// Do not move this. We need to override the define from pro.h -#define CASSERT(type) - -// Convert all of these -%cstring_output_maxstr_none(char *buf, size_t bufsize); -%binary_output_or_none(void *buf, size_t bufsize); -%binary_output_with_size(void *buf, size_t *bufsize); - -// Accept single Python string for const void * + size input arguments -// For example: put_many_bytes() and patch_many_bytes() -%apply (char *STRING, int LENGTH) { (const void *buf, size_t size) }; -%apply (char *STRING, int LENGTH) { (const void *buf, size_t len) }; -%apply (char *STRING, int LENGTH) { (const void *value, size_t length) }; -%apply (char *STRING, int LENGTH) { (const void *dataptr,size_t len) }; - -// Create wrapper classes for basic type arrays -%array_class(uchar, uchar_array); -%array_class(tid_t, tid_array); -%array_class(ea_t, ea_array); -%array_class(sel_t, sel_array); -%array_class(uval_t, uval_array); -%pointer_class(int, int_pointer); -%pointer_class(ea_t, ea_pointer); -%pointer_class(sval_t, sval_pointer); -%pointer_class(sel_t, sel_pointer); - -%include "ida.i" -%include "idd.i" -%include "idp.i" -%include "netnode.i" -%include "nalt.i" - -%include "allins.i" -%include "area.i" -%include "auto.i" -%include "bytes.i" -%include "dbg.i" -%include "diskio.i" -%include "entry.i" -%include "enum.i" -%include "expr.i" -%include "fixup.i" -%include "frame.i" -%include "funcs.i" - -%inline { -/* Small wrapper to get the inf structure */ -idainfo *get_inf_structure(void) -{ - return &inf; -} -} - -%include "gdl.i" -%include "ints.i" -%include "kernwin.i" -%include "lines.i" -%include "loader.i" -%include "moves.i" -%include "name.i" -%include "offset.i" -%include "queue.i" -%include "search.i" -%include "segment.i" -%include "srarea.i" -%include "strlist.i" -%include "struct.i" -%include "typeinf.i" -%include "ua.i" -%include "xref.i" -#ifdef __NT__ - %include "graph.i" -#endif -%include "fpro.i" - -%inline { - void set_script_timeout(int timeout); - void enable_extlang_python(bool enable); - void enable_python_cli(bool enable); -} - -%pythoncode %{ -# -import struct -# ----------------------------------------------------------------------- -# Seek constants -SEEK_SET = 0 # from the file start -SEEK_CUR = 1 # from the current position -SEEK_END = 2 # from the file end - -# ----------------------------------------------------------------------- -# This is a special helper object that helps detect which kind -# of object is this python object wrapping and how to convert it -# back and from IDC. -# This object is characterized by its special attribute and its value -class PyIdc_cvt_helper__(object): - def __init__(self, cvt_id, value): - # 0 = int64 object - # 1 = byref object - # 2 = opaque object - self.__idc_cvt_id__ = cvt_id - self.value = value - - def __set_value(self, v): - self.__idc_cvt_value__ = v - def __get_value(self): - return self.__idc_cvt_value__ - value = property(__get_value, __set_value) - -# ----------------------------------------------------------------------- -class PyIdc_cvt_int64__(PyIdc_cvt_helper__): - """Helper class for explicitly representing VT_INT64 values""" - def __init__(self, v): - # id = 0 = int64 object - super(self.__class__, self).__init__(0, v) - - # operation table - op_table = \ - { - 0: lambda a, b: a + b, - 1: lambda a, b: a - b, - 2: lambda a, b: a * b, - 3: lambda a, b: a / b - } - # carries the operation given its number - def op(self, op_n, other, rev=False): - a = self.value - # other operand of same type? then take its value field - if type(other) == type(self): - b = other.value - else: - b = other - if rev: - t = a - a = b - b = t - # construct a new object and return as the result - return self.__class__(self.op_table[op_n](a, b)) - - # overloaded operators - def __add__(self, other): return self.op(0, other) - def __sub__(self, other): return self.op(1, other) - def __mul__(self, other): return self.op(2, other) - def __div__(self, other): return self.op(3, other) - def __radd__(self, other): return self.op(0, other, True) - def __rsub__(self, other): return self.op(1, other, True) - def __rmul__(self, other): return self.op(2, other, True) - def __rdiv__(self, other): return self.op(3, other, True) - -# ----------------------------------------------------------------------- -class PyIdc_cvt_refclass__(PyIdc_cvt_helper__): - """Helper class for representing references to immutable objects""" - def __init__(self, v): - # id = one = byref object - super(self.__class__, self).__init__(1, v) - - def cstr(self): - return as_cstr(self.value) - -# ----------------------------------------------------------------------- -# This object can be passed to IDC and back to Python transparently -# The attribute "__idc_cvt_value__" is used -class PyIdc_cvt_opaque__(PyIdc_cvt_helper__): - def __init__(self, v): - # id = two = opaque object - super(self.__class__, self).__init__(2, v) - -# ----------------------------------------------------------------------- -def as_cstr(val): - """ - Returns a C str from the passed value. The passed value can be of type refclass (returned by a call to buffer() or byref()) - It scans for the first \x00 and returns the string value up to that point. - """ - if isinstance(val, PyIdc_cvt_refclass__): - val = val.value - n = 0 - for x in val: - if ord(x) == 0: - break - n = n + 1 - return val[:n] - -# ----------------------------------------------------------------------- -def as_unicode(s): - """Convenience function to convert a string into appropriate unicode format""" - # use UTF16 big/little endian, depending on the environment? - return unicode(s).encode("UTF-16" + ("BE" if _idaapi.cvar.inf.mf else "LE")) - -# ----------------------------------------------------------------------- -def as_uint32(v): - return v & 0xffffffff - -# ----------------------------------------------------------------------- -def as_int32(v): - return -((~v & 0xffffffff)+1) - -# ----------------------------------------------------------------------- -def as_signed(v, nbits = 32): - return -(( ~v & ((1 << nbits)-1) ) + 1) if v & (1 << nbits-1) else v - -# ---------------------------------------------------------------------- -# Copy bits from a value -def copy_bits(b, s, e=-1): - # end-bit not specified? use start bit (thus extract one bit) - if e == -1: - e = s - # swap start and end if start > end - if s > e: - e, s = s, e - - mask = 0 - for i in xrange(s, e+1): - mask |= 1 << i - - return (b & mask) >> s - -# ---------------------------------------------------------------------- -struct_unpack_table = { - 1: ('b', 'B'), - 2: ('h', 'H'), - 4: ('l', 'L'), - 8: ('q', 'Q') -} - -# ---------------------------------------------------------------------- -def struct_unpack(value, signed = False, offs = 0): - """ - Unpack a value given its length and offset using struct.unpack_from(). - This function will know how to unpack the given value by using the lookup table 'struct_unpack_table' - """ - # Supported length? - n = len(value) - if not n in struct_unpack_table: - return None - # Conver to number - signed = 1 if signed else 0 - - # Unpack - return struct.unpack_from(struct_unpack_table[n][signed], value, offs)[0] - - - -# - -%} \ No newline at end of file +%module(docstring="IDA Pro Plugin SDK API wrapper",directors="1") idaapi +// Suppress 'previous definition of XX' warnings +#pragma SWIG nowarn=302 +// and others... +#pragma SWIG nowarn=312 +#pragma SWIG nowarn=314 +#pragma SWIG nowarn=362 +#pragma SWIG nowarn=383 +#pragma SWIG nowarn=389 +#pragma SWIG nowarn=401 +#pragma SWIG nowarn=451 +#pragma SWIG nowarn=454 // Setting a pointer/reference variable may leak memory + +// Enable automatic docstring generation +%feature(autodoc); + +// We use those special maps because SWIG wraps passed PyObject* with 'SwigPtr_PyObject' and 'SwigVar_PyObject' +// They act like autoptr and decrement the reference of the object when the scope ends +// We need to keep a reference outside SWIG and let the caller manage its references +%typemap(directorin) PyObject * "/*%din%*/Py_XINCREF($1_name);$input = $1_name;" +%typemap(directorout) PyObject * "/*%dout%*/$result = result;Py_XINCREF($result);" +%{ +#include + +#ifndef USE_DANGEROUS_FUNCTIONS + #define USE_DANGEROUS_FUNCTIONS 1 +#endif + +#ifndef NO_OBSOLETE_FUNCS + #define NO_OBSOLETE_FUNCS 1 +#endif + +#ifdef HAVE_SSIZE_T +#define _SSIZE_T_DEFINED 1 +#endif +#if defined(__NT__) && !defined(_WINDOWS_) + #define _WINDOWS_ // kernwin.hpp needs it to declare create_tform() + typedef void *HWND; // we don't need to include windows.h for just this definition +#endif +#include "ida.hpp" +#include "idp.hpp" +#include "allins.hpp" +#include "auto.hpp" +#include "bytes.hpp" +#include "dbg.hpp" +#include "diskio.hpp" +#include "entry.hpp" +#include "enum.hpp" +#include "expr.hpp" +#include "frame.hpp" +#include "fixup.hpp" +#include "funcs.hpp" +#include "gdl.hpp" +#include "idd.hpp" +#include "ints.hpp" +#include "kernwin.hpp" +#include "lines.hpp" +#include "loader.hpp" +#include "moves.hpp" +#include "netnode.hpp" +#include "nalt.hpp" +#include "name.hpp" +#include "offset.hpp" +#include "queue.hpp" +#include "search.hpp" +#include "srarea.hpp" +#include "strlist.hpp" +#include "struct.hpp" +#include "typeinf.hpp" +#include "ua.hpp" +#include "xref.hpp" +#include "ieee.h" +#include "err.h" +#include "fpro.h" +#include +#ifdef __NT__ + #include "graph.hpp" +#endif + +// + +#include "pywraps.hpp" + +//------------------------------------------------------------------------ +// String constants used +static const char S_PY_SWIEX_CLSNAME[] = "switch_info_ex_t"; +static const char S_PY_OP_T_CLSNAME[] = "op_t"; +static const char S_PY_IDC_GLOBAL_VAR_FMT[] = "__py_cvt_gvar_%d"; +static const char S_PY_IDCCVT_ID_ATTR[] = "__idc_cvt_id__"; +static const char S_PY_IDCCVT_VALUE_ATTR[] = "__idc_cvt_value__"; +static const char S_PY_IDC_OPAQUE_T[] = "py_idc_cvt_helper_t"; +static const char S_PROPS[] = "props"; +static const char S_NAME[] = "name"; +static const char S_ASM_KEYWORD[] = "asm_keyword"; +static const char S_MENU_NAME[] = "menu_name"; +static const char S_HOTKEY[] = "hotkey"; +static const char S_VALUE_SIZE[] = "value_size"; +static const char S_MAY_CREATE_AT[] = "may_create_at"; +static const char S_CALC_ITEM_SIZE[] = "calc_item_size"; +static const char S_ID[] = "id"; +static const char S_PRINTF[] = "printf"; +static const char S_TEXT_WIDTH[] = "text_width"; +static const char S_SCAN[] = "scan"; +static const char S_ANALYZE[] = "analyze"; +static const char S_CBSIZE[] = "cbsize"; +static const char S_ON_CLICK[] = "OnClick"; +static const char S_ON_CLOSE[] = "OnClose"; +static const char S_ON_DBL_CLICK[] = "OnDblClick"; +static const char S_ON_CURSOR_POS_CHANGED[] = "OnCursorPosChanged"; +static const char S_ON_KEYDOWN[] = "OnKeydown"; +static const char S_ON_POPUP[] = "OnPopup"; +static const char S_ON_HINT[] = "OnHint"; +static const char S_ON_POPUP_MENU[] = "OnPopupMenu"; +static const char S_ON_EDIT_LINE[] = "OnEditLine"; +static const char S_ON_INSERT_LINE[] = "OnInsertLine"; +static const char S_ON_GET_LINE[] = "OnGetLine"; +static const char S_ON_DELETE_LINE[] = "OnDeleteLine"; +static const char S_ON_REFRESH[] = "OnRefresh"; +static const char S_ON_SELECT_LINE[] = "OnSelectLine"; +static const char S_ON_COMMAND[] = "OnCommand"; +static const char S_ON_GET_ICON[] = "OnGetIcon"; +static const char S_ON_GET_LINE_ATTR[] = "OnGetLineAttr"; +static const char S_ON_GET_SIZE[] = "OnGetSize"; +static const char S_ON_GETTEXT[] = "OnGetText"; +static const char S_ON_ACTIVATE[] = "OnActivate"; +static const char S_ON_DEACTIVATE[] = "OnDeactivate"; +static const char S_ON_SELECT[] = "OnSelect"; +static const char S_M_EDGES[] = "_edges"; +static const char S_M_NODES[] = "_nodes"; +static const char S_M_THIS[] = "_this"; +static const char S_M_TITLE[] = "_title"; +static const char S_CLINK_NAME[] = "__clink__"; + +#ifdef __PYWRAPS__ +static const char S_PY_IDAAPI_MODNAME[] = "__main__"; +#else +static const char S_PY_IDAAPI_MODNAME[] = S_IDAAPI_MODNAME; +#endif + +//------------------------------------------------------------------------ +// Constants used by get_idaapi_class_reference() +#define PY_CLSID_CVT_INT64 0 +#define PY_CLSID_APPCALL_SKEL_OBJ 1 +#define PY_CLSID_CVT_BYREF 2 +#define PY_CLSID_LAST 3 + +//------------------------------------------------------------------------ +static PyObject *py_cvt_helper_module = NULL; +static bool pywraps_initialized = false; + +//------------------------------------------------------------------------ +static idc_class_t *get_py_idc_cvt_opaque() +{ + return find_idc_class(S_PY_IDC_OPAQUE_T); +} + +//------------------------------------------------------------------------- +// Utility function to convert a python object to an IDC object +// and sets a python exception on failure. +bool convert_pyobj_to_idc_exc(PyObject *py_obj, idc_value_t *idc_obj) +{ + int sn = 0; + if ( pyvar_to_idcvar(py_obj, idc_obj, &sn) < CIP_OK ) + { + PyErr_SetString(PyExc_ValueError, "Could not convert Python object to IDC object!"); + return false; + } + return true; +} + +//------------------------------------------------------------------------ +// IDC Opaque object destructor: when the IDC object dies we kill the +// opaque Python object along with it +static const char py_idc_cvt_helper_dtor_args[] = { VT_OBJ, 0 }; +static error_t idaapi py_idc_opaque_dtor( + idc_value_t *argv, + idc_value_t *res) +{ + // Get the value from the object + idc_value_t idc_val; + VarGetAttr(&argv[0], S_PY_IDCCVT_VALUE_ATTR, &idc_val); + + // Extract the Python object reference + PyObject *py_obj = (PyObject *)idc_val.pvoid; + // Decrease its reference (and eventually destroy it) + Py_DECREF(py_obj); + return eOk; +} + +//------------------------------------------------------------------------ +// This function must be called on initialization +bool init_pywraps() +{ + if ( pywraps_initialized ) + return true; + + // Take a reference to the idaapi python module + // (We need it to create instances of certain classes) + if ( py_cvt_helper_module == NULL ) + { + // Take a reference to the module so we can create the needed class instances + py_cvt_helper_module = PyImport_TryImportModule(S_PY_IDAAPI_MODNAME); + if ( py_cvt_helper_module == NULL ) + return false; + } + + if ( get_py_idc_cvt_opaque() == NULL ) + { + // Add the class + idc_class_t *idc_cvt_opaque = add_idc_class(S_PY_IDC_OPAQUE_T); + if ( idc_cvt_opaque == NULL ) + return false; + + // Form the dtor name + char dtor_name[MAXSTR]; + qsnprintf(dtor_name, sizeof(dtor_name), "%s.dtor", S_PY_IDC_OPAQUE_T); + + // register the dtor function + if ( !set_idc_func_ex(dtor_name, py_idc_opaque_dtor, py_idc_cvt_helper_dtor_args, 0) ) + return false; + + // Link the dtor function to the class + set_idc_dtor(idc_cvt_opaque, dtor_name); + } + pywraps_initialized = true; + return true; +} + +//------------------------------------------------------------------------ +// This function must be called on de-initialization +void deinit_pywraps() +{ + if ( !pywraps_initialized ) + return; + pywraps_initialized = false; + Py_XDECREF(py_cvt_helper_module); + py_cvt_helper_module = NULL; +} + +//------------------------------------------------------------------------ +// Utility function to create a class instance whose constructor takes zero arguments +PyObject *create_idaapi_class_instance0(const char *clsname) +{ + PyObject *py_cls = get_idaapi_attr(clsname); + if ( py_cls == NULL ) + return NULL; + PyObject *py_obj = PyObject_CallFunctionObjArgs(py_cls, NULL); + Py_DECREF(py_cls); + if ( PyGetError() || py_obj == NULL ) + { + Py_XDECREF(py_obj); + Py_RETURN_NONE; + } + return py_obj; +} + +//------------------------------------------------------------------------ +// Utility function to create linked class instances +PyObject *create_idaapi_linked_class_instance(const char *clsname, void *lnk) +{ + PyObject *py_cls = get_idaapi_attr(clsname); + if ( py_cls == NULL ) + return NULL; + + PyObject *py_lnk = PyCObject_FromVoidPtr(lnk, NULL); + PyObject *py_obj = PyObject_CallFunctionObjArgs(py_cls, py_lnk, NULL); + Py_DECREF(py_cls); + Py_DECREF(py_lnk); + + if ( PyGetError() || py_obj == NULL ) + { + Py_XDECREF(py_obj); + py_obj = NULL; + } + return py_obj; +} + +//------------------------------------------------------------------------ +// Gets a class type reference in idaapi +// With the class type reference we can create a new instance of that type +// This function takes a reference to the idaapi module and keeps the reference +static PyObject *get_idaapi_attr(const int class_id) +{ + if ( class_id >= PY_CLSID_LAST ) + return NULL; + + // Some class names. The array is parallel with the PY_CLSID_xxx consts + static const char *class_names[]= + { + "PyIdc_cvt_int64__", + "object_t", + "PyIdc_cvt_refclass__" + }; + return PyObject_GetAttrString(py_cvt_helper_module, class_names[class_id]); +} + +//------------------------------------------------------------------------ +// Gets a class reference by name +PyObject *get_idaapi_attr(const char *attrname) +{ + return PyObject_TryGetAttrString(py_cvt_helper_module, attrname); +} +//------------------------------------------------------------------------ +// Returns an attribute or NULL +// No errors will be set if the attribute did not exist +PyObject *PyObject_TryGetAttrString(PyObject *py_var, const char *attr) +{ + if ( !PyObject_HasAttrString(py_var, attr) ) + return NULL; + return PyObject_GetAttrString(py_var, attr); +} + +//------------------------------------------------------------------------ +// Tries to import a module and clears the exception on failure +PyObject *PyImport_TryImportModule(const char *name) +{ + PyObject *result = PyImport_ImportModule(name); + if ( result != NULL ) + return result; + if ( PyErr_Occurred() ) + PyErr_Clear(); + return NULL; +} + +//------------------------------------------------------------------------- +// Converts a Python number into an IDC value (32 or 64bits) +// The function will first try to convert the number into a 32bit value +// If the number does not fit then VT_INT64 will be used +// NB: This function cannot properly detect if the Python value should be +// converted to a VT_INT64 or not. For example: 2**32-1 = 0xffffffff which +// can fit in a C long but Python creates a PyLong object for it. +// And because of that we are confused as to whether to convert to 32 or 64 +bool PyGetNumberAsIDC(PyObject *py_var, idc_value_t *idc_var) +{ + if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) + return false; + + // Can we convert to C long? + long l = PyInt_AsLong(py_var); + if ( !PyErr_Occurred() ) + { + idc_var->set_long(l); + return true; + } + // Clear last error + PyErr_Clear(); + // Can be fit into a C unsigned long? + l = (long) PyLong_AsUnsignedLong(py_var); + if ( !PyErr_Occurred() ) + { + idc_var->set_long(l); + return true; + } + PyErr_Clear(); + idc_var->set_int64(PyLong_AsLongLong(py_var)); + return true; +} + +//------------------------------------------------------------------------- +// Parses a Python object as a long or long long +bool PyGetNumber(PyObject *py_var, uint64 *num, bool *is_64) +{ + if ( !(PyInt_CheckExact(py_var) || PyLong_CheckExact(py_var)) ) + return false; + + // Can we convert to C long? + long l = PyInt_AsLong(py_var); + if ( !PyErr_Occurred() ) + { + if ( num != NULL ) + *num = uint64(l); + if ( is_64 != NULL ) + *is_64 = false; + return true; + } + // Clear last error + PyErr_Clear(); + // Can be fit into a C unsigned long? + unsigned long ul = PyLong_AsUnsignedLong(py_var); + if ( !PyErr_Occurred() ) + { + if ( num != NULL ) + *num = uint64(ul); + if ( is_64 != NULL ) + *is_64 = false; + return true; + } + PyErr_Clear(); + PY_LONG_LONG ll = PyLong_AsLongLong(py_var); + if ( !PyErr_Occurred() ) + { + if ( num != NULL ) + *num = uint64(ll); + if ( is_64 != NULL ) + *is_64 = true; + return true; + } + PyErr_Clear(); + return false; +} + +//------------------------------------------------------------------------- +// Checks if a given object is of sequence type +bool PyIsSequenceType(PyObject *obj) +{ + if ( !PySequence_Check(obj) ) + return false; + Py_ssize_t sz = PySequence_Size(obj); + if ( sz == -1 || PyErr_Occurred() != NULL ) + { + PyErr_Clear(); + return false; + } + return true; +} + +//------------------------------------------------------------------------- +// Returns the string representation of an object +bool PyObjectToString(PyObject *obj, qstring *out) +{ + PyObject *py_str = PyObject_Str(obj); + if ( py_str != NULL ) + { + *out = PyString_AsString(py_str); + Py_DECREF(py_str); + return true; + } + else + { + out->qclear(); + return false; + } +} + +//-------------------------------------------------------------------------- +// Checks if a Python error occured and fills the out parameter with the +// exception string +bool PyGetError(qstring *out) +{ + if ( !PyErr_Occurred() ) + return false; + + PyObject *err_type, *err_value, *err_traceback; + PyErr_Fetch(&err_type, &err_value, &err_traceback); + if ( out != NULL ) + PyObjectToString(err_value, out); + return true; +} + +//------------------------------------------------------------------------- +// A loud version of PyGetError() which gets the error and displays it +bool PyShowErr(const char *cb_name) +{ + static qstring err_str; + if ( !PyGetError(&err_str) ) + return false; + warning("IDAPython: Error while calling Python callback <%s>:\n%s", cb_name, err_str.c_str()); + return true; +} + +//------------------------------------------------------------------------- +// Checks if the given py_var is a special PyIdc_cvt_helper object. +// It does that by examining the magic attribute and returns its numeric value. +// It returns -1 if the object is not a recognized helper object. +// Any Python object can be treated as an cvt object if this attribute is created. +static int get_pyidc_cvt_type(PyObject *py_var) +{ + // Check if this our special by reference object + PyObject *attr = PyObject_TryGetAttrString(py_var, S_PY_IDCCVT_ID_ATTR); + if ( attr == NULL ) + return -1; + if ( !(PyInt_Check(attr) || PyLong_Check(attr)) ) + { + Py_DECREF(attr); + return -1; + } + int r = (int)PyInt_AsLong(attr); + Py_DECREF(attr); + return r; +} + +//------------------------------------------------------------------------- +// Utility function to create opaque / convertible Python <-> IDC variables +// The referred Python variable will have its reference increased +static bool create_py_idc_opaque_obj(PyObject *py_var, idc_value_t *idc_var) +{ + // Create an IDC object of this special helper class + if ( VarObject(idc_var, get_py_idc_cvt_opaque()) != eOk ) + return false; + + // Store the CVT id + idc_value_t idc_val; + idc_val.set_long(PY_ICID_OPAQUE); + VarSetAttr(idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val); + + // Store the value as a PVOID referencing the given Python object + idc_val.set_pvoid(py_var); + VarSetAttr(idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val); + + return true; +} + +//------------------------------------------------------------------------- +Py_ssize_t pyvar_walk_list( + PyObject *py_list, + int (idaapi *cb)(PyObject *py_item, Py_ssize_t index, void *ud), + void *ud) +{ + if ( !PyList_CheckExact(py_list) && !PyIsSequenceType(py_list) ) + return CIP_FAILED; + + bool is_seq = !PyList_CheckExact(py_list); + Py_ssize_t size = is_seq ? PySequence_Size(py_list) : PyList_Size(py_list); + + if ( cb == NULL ) + return size; + + Py_ssize_t i; + for ( i=0; iset_long(0); + // Numbers? + else if ( PyGetNumberAsIDC(py_var, idc_var) ) + return CIP_OK; + // String + else if ( PyString_Check(py_var) ) + idc_var->_set_string(PyString_AsString(py_var), PyString_Size(py_var)); + // Float + else if ( PyBool_Check(py_var) ) + idc_var->set_long(py_var == Py_True ? 1 : 0); + // Boolean + else if ( PyFloat_Check(py_var) ) + { + double dresult = PyFloat_AsDouble(py_var); + ieee_realcvt((void *)&dresult, idc_var->e, 3); + idc_var->vtype = VT_FLOAT; + } + // void* + else if ( PyCObject_Check(py_var) ) + idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var)); + // Is it a Python list? + else if ( PyList_CheckExact(py_var) || PyIsSequenceType(py_var) ) + { + // Create the object + VarObject(idc_var); + + // Determine list size and type + bool is_seq = !PyList_CheckExact(py_var); + Py_ssize_t size = is_seq ? PySequence_Size(py_var) : PyList_Size(py_var); + bool ok = true; + qstring attr_name; + + // Convert each item + for ( Py_ssize_t i=0; i= CIP_OK; + if ( ok ) + { + // Form the attribute name + PyObject *py_int = PyInt_FromSsize_t(i); + ok = PyObjectToString(py_int, &attr_name); + if ( !ok ) + break; + Py_DECREF(py_int); + // Store the attribute + VarSetAttr(idc_var, attr_name.c_str(), &v); + } + // Sequences return a new reference for GetItem() + if ( is_seq ) + Py_DECREF(py_var); + if ( !ok ) + break; + } + return ok ? CIP_OK : CIP_FAILED; + } + // Dictionary: we convert to an IDC object + else if ( PyDict_Check(py_var) ) + { + // Create an empty IDC object + VarObject(idc_var); + + // Get the dict.items() list + PyObject *py_items = PyDict_Items(py_var); + + // Get the size of the list + qstring key_name; + bool ok = true; + Py_ssize_t size = PySequence_Size(py_items); + for ( Py_ssize_t i=0; i (key, value) + PyObject *py_item = PyList_GetItem(py_items, i); + + // Extract key/value + PyObject *key = PySequence_GetItem(py_item, 0); + PyObject *val = PySequence_GetItem(py_item, 1); + + // Get key's string representation + PyObjectToString(key, &key_name); + + // Convert the attribute into an IDC value + idc_value_t v; + ok = pyvar_to_idcvar(val, &v, gvar_sn) >= CIP_OK; + if ( ok ) + { + // Store the attribute + VarSetAttr(idc_var, key_name.c_str(), &v); + } + Py_XDECREF(key); + Py_XDECREF(val); + if ( !ok ) + break; + } + // Decrement attribute reference + Py_DECREF(py_items); + return ok ? CIP_OK : CIP_FAILED; + } + // Possible function? + else if ( PyCallable_Check(py_var) ) + { + idc_var->clear(); + idc_var->vtype = VT_FUNC; + idc_var->funcidx = -1; // Does not apply + return CIP_OK; + } + // Objects: + // - pyidc_cvt objects: int64, byref, opaque + // - other python objects + else + { + // Get the type + int cvt_id = get_pyidc_cvt_type(py_var); + switch ( cvt_id ) + { + // + // INT64 + // + case PY_ICID_INT64: + // Get the value attribute + attr = PyObject_TryGetAttrString(py_var, S_PY_IDCCVT_VALUE_ATTR); + if ( attr == NULL ) + return false; + idc_var->set_int64(PyLong_AsLongLong(attr)); + Py_DECREF(attr); + return CIP_OK; + // + // BYREF + // + case PY_ICID_BYREF: + { + // BYREF always require this parameter + if ( gvar_sn == NULL ) + return CIP_FAILED; + + // Get the value attribute + attr = PyObject_TryGetAttrString(py_var, S_PY_IDCCVT_VALUE_ATTR); + if ( attr == NULL ) + return CIP_FAILED; + + // Create a global variable + char buf[MAXSTR]; + qsnprintf(buf, sizeof(buf), S_PY_IDC_GLOBAL_VAR_FMT, *gvar_sn); + idc_value_t *gvar = add_idc_gvar(buf); + // Convert the python value into the IDC global variable + bool ok = pyvar_to_idcvar(attr, gvar, gvar_sn) >= CIP_OK; + if ( ok ) + { + (*gvar_sn)++; + // Create a reference to this global variable + VarRef(idc_var, gvar); + } + Py_DECREF(attr); + return ok ? CIP_OK : CIP_FAILED; + } + // + // OPAQUE + // + case PY_ICID_OPAQUE: + { + if ( !create_py_idc_opaque_obj(py_var, idc_var) ) + return CIP_FAILED; + return CIP_OK_NODECREF; + } + // + // Other objects + // + default: + // A normal object? + PyObject *py_dir = PyObject_Dir(py_var); + Py_ssize_t size = PyList_Size(py_dir); + if ( py_dir == NULL || !PyList_Check(py_dir) || size == 0 ) + { + Py_XDECREF(py_dir); + return CIP_FAILED; + } + // Create the IDC object + VarObject(idc_var); + for ( Py_ssize_t i=0; i 2 ) + && (strncmp(field_name, "__", 2) == 0 ) + && (strncmp(field_name+len-2, "__", 2) == 0) ) + { + continue; + } + idc_value_t v; + // Get the non-private attribute from the object + attr = PyObject_GetAttrString(py_var, field_name); + if (attr == NULL + // Convert the attribute into an IDC value + || pyvar_to_idcvar(attr, &v, gvar_sn) < CIP_OK) + { + Py_XDECREF(attr); + return CIP_FAILED; + } + // Store the attribute + VarSetAttr(idc_var, field_name, &v); + // Decrement attribute reference + Py_DECREF(attr); + } + } + } + return CIP_OK; +} + +//------------------------------------------------------------------------- +// Converts an IDC variable to a Python variable +// If py_var points to an existing object then the object will be updated +// If py_var points to an existing immutable object then ZERO is returned +// Returns one of CIP_xxxx. Check pywraps.hpp +int idcvar_to_pyvar( + const idc_value_t &idc_var, + PyObject **py_var) +{ + switch ( idc_var.vtype ) + { + case VT_PVOID: + if ( *py_var == NULL ) + *py_var = PyCObject_FromVoidPtr(idc_var.pvoid, NULL); + else + return CIP_IMMUTABLE; + break; + case VT_INT64: + { + // Recycle? + if ( *py_var != NULL ) + { + // Recycling an int64 object? + int t = get_pyidc_cvt_type(*py_var); + if ( t != PY_ICID_INT64 ) + return CIP_IMMUTABLE; // Cannot recycle immutable object + // Update the attribute + PyObject_SetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64)); + return CIP_OK; + } + PyObject *py_cls = get_idaapi_attr(PY_CLSID_CVT_INT64); + if ( py_cls == NULL ) + return CIP_FAILED; + *py_var = PyObject_CallFunctionObjArgs(py_cls, PyLong_FromLongLong(idc_var.i64), NULL); + Py_DECREF(py_cls); + if ( PyGetError() || *py_var == NULL ) + return CIP_FAILED; + break; + } +#if !defined(NO_OBSOLETE_FUNCS) || defined(__EXPR_SRC) + case VT_STR: + *py_var = PyString_FromString(idc_var.str); + break; +#endif + case VT_STR2: + if ( *py_var == NULL ) + { + const qstring &s = idc_var.qstr(); + *py_var = PyString_FromStringAndSize(s.begin(), s.length()); + break; + } + else + return CIP_IMMUTABLE; // Cannot recycle immutable object + case VT_LONG: + // Cannot recycle immutable objects + if ( *py_var != NULL ) + return CIP_IMMUTABLE; +#ifdef __EA64__ + *py_var = PyLong_FromLongLong(idc_var.num); +#else + *py_var = PyLong_FromLong(idc_var.num); +#endif + break; + case VT_FLOAT: + if ( *py_var == NULL ) + { + double x; + if ( ph.realcvt(&x, (uint16 *)idc_var.e, (sizeof(x)/2-1)|010) != 0 ) + INTERR(); + *py_var = PyFloat_FromDouble(x); + break; + } + else + return CIP_IMMUTABLE; + case VT_REF: + { + if ( *py_var == NULL ) + { + PyObject *py_cls = get_idaapi_attr(PY_CLSID_CVT_BYREF); + if ( py_cls == NULL ) + return CIP_FAILED; + // Create a byref object with None value. We populate it later + *py_var = PyObject_CallFunctionObjArgs(py_cls, Py_None, NULL); + Py_DECREF(py_cls); + if ( PyGetError() || *py_var == NULL ) + return CIP_FAILED; + } + int t = *py_var == NULL ? -1 : get_pyidc_cvt_type(*py_var); + if ( t != PY_ICID_BYREF ) + return CIP_FAILED; + + // Dereference + // (Since we are not using VREF_COPY flag, we can safely const_cast) + idc_value_t *dref_v = VarDeref(const_cast(&idc_var), VREF_LOOP); + if ( dref_v == NULL ) + return CIP_FAILED; + + // Can we recycle the object? + PyObject *new_py_val = PyObject_TryGetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR); + if ( new_py_val != NULL ) + { + // Recycle + t = idcvar_to_pyvar(*dref_v, &new_py_val); + Py_XDECREF(new_py_val); // DECREF because of GetAttrStr + // Success? Nothing more to be done + if ( t == CIP_OK ) + return CIP_OK; + // Clear it so we don't recycle it + new_py_val = NULL; + } + // Try to convert (not recycle) + if ( idcvar_to_pyvar(*dref_v, &new_py_val) != CIP_OK ) + return CIP_FAILED; + // Update the attribute + PyObject_SetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR, new_py_val); + Py_DECREF(new_py_val); + break; + } + // Can convert back into a Python object or Python dictionary + // (Depending if py_var will be recycled and it was a dictionary) + case VT_OBJ: + { + // Check if this IDC object has __cvt_id__ and the __idc_cvt_value__ fields + idc_value_t idc_val; + if ( VarGetAttr(&idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val) == eOk + && VarGetAttr(&idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val) == eOk ) + { + // Extract the object + *py_var = (PyObject *) idc_val.pvoid; + return CIP_OK_NODECREF; + } + PyObject *obj; + bool is_dict = false; + // Need to create a new object? + if ( *py_var == NULL ) + { + PyObject *py_cls = get_idaapi_attr(PY_CLSID_APPCALL_SKEL_OBJ); + if ( py_cls == NULL ) + return CIP_FAILED; + obj = PyObject_CallFunctionObjArgs(py_cls, NULL); + Py_DECREF(py_cls); + if ( PyGetError() || obj == NULL ) + return CIP_FAILED; + } + else + { + // Recycle existing variable + obj = *py_var; + if ( PyDict_Check(obj) ) + is_dict = true; + } + // Walk the IDC attributes and store into python + for (const char *attr_name = VarFirstAttr(&idc_var); + attr_name != NULL; + attr_name=VarNextAttr(&idc_var, attr_name)) + { + // Get the attribute + idc_value_t v; + VarGetAttr(&idc_var, attr_name, &v, true); + // Convert attribute to a python value + PyObject *py_attr(NULL); + int cvt = idcvar_to_pyvar(v, &py_attr); + if ( cvt <= CIP_IMMUTABLE ) + { + // Delete the object (if we created it) + if ( *py_var == NULL ) + Py_DECREF(obj); + return CIP_FAILED; + } + if ( is_dict ) + PyDict_SetItemString(obj, attr_name, py_attr); + else + PyObject_SetAttrString(obj, attr_name, py_attr); + if ( cvt == CIP_OK ) + Py_XDECREF(py_attr); + } + *py_var = obj; + break; + } + // Unhandled type + default: + *py_var = NULL; + return CIP_FAILED; + } + return CIP_OK; +} + + + +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +class pywraps_notify_when_t +{ + ppyobject_vec_t table[NW_EVENTSCNT]; + qstring err; + bool in_notify; + struct notify_when_args_t + { + int when; + PyObject *py_callable; + }; + typedef qvector notify_when_args_vec_t; + notify_when_args_vec_t delayed_notify_when_list; + + //------------------------------------------------------------------------ + static int idaapi idp_callback(void *ud, int event_id, va_list va) + { + pywraps_notify_when_t *_this = (pywraps_notify_when_t *)ud; + switch ( event_id ) + { + case processor_t::newfile: + case processor_t::oldfile: + { + int old = event_id == processor_t::oldfile ? 1 : 0; + char *dbname = va_arg(va, char *); + _this->notify(NW_OPENIDB_SLOT, old); + } + break; + case processor_t::closebase: + _this->notify(NW_CLOSEIDB_SLOT); + break; + } + // event not processed, let other plugins or the processor module handle it + return 0; + } + + //------------------------------------------------------------------------ + bool unnotify_when(int when, PyObject *py_callable) + { + int cnt = 0; + for ( int slot=0; slot 0; + } + + //------------------------------------------------------------------------ + void register_callback(int slot, PyObject *py_callable) + { + ppyobject_vec_t &tbl = table[slot]; + ppyobject_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, py_callable); + // Already added + if ( it != it_end ) + return; + + // Increment reference + Py_INCREF(py_callable); + + // Insert the element + tbl.push_back(py_callable); + } + + //------------------------------------------------------------------------ + void unregister_callback(int slot, PyObject *py_callable) + { + ppyobject_vec_t &tbl = table[slot]; + ppyobject_vec_t::iterator it_end = tbl.end(), it = std::find(tbl.begin(), it_end, py_callable); + // Not found? + if ( it == it_end ) + return; + + // Decrement reference + Py_DECREF(py_callable); + + // Delete the element + tbl.erase(it); + } + +public: + //------------------------------------------------------------------------ + bool init() + { + return hook_to_notification_point(HT_IDP, idp_callback, this); + } + + //------------------------------------------------------------------------ + bool deinit() + { + // Uninstall all objects + ppyobject_vec_t::iterator it, it_end; + for ( int slot=0; slot 0; + } + + //------------------------------------------------------------------------ + bool notify(int slot, ...) + { + va_list va; + va_start(va, slot); + bool ok = notify_va(slot, va); + va_end(va); + return ok; + } + + //------------------------------------------------------------------------ + bool notify_va(int slot, va_list va) + { + // Sanity bounds check! + if ( slot < 0 || slot >= NW_EVENTSCNT ) + return false; + + bool ok = true; + in_notify = true; + int old = slot == NW_OPENIDB_SLOT ? va_arg(va, int) : 0; + for (ppyobject_vec_t::iterator it = table[slot].begin(), it_end = table[slot].end(); + it != it_end; + ++it) + { + // Form the notification code + PyObject *py_code = PyInt_FromLong(1 << slot); + PyObject *py_result(NULL); + switch ( slot ) + { + case NW_CLOSEIDB_SLOT: + case NW_INITIDA_SLOT: + case NW_TERMIDA_SLOT: + py_result = PyObject_CallFunctionObjArgs(*it, py_code, NULL); + break; + case NW_OPENIDB_SLOT: + { + PyObject *py_old = PyInt_FromLong(old); + py_result = PyObject_CallFunctionObjArgs(*it, py_code, py_old, NULL); + Py_DECREF(py_old); + } + break; + } + Py_DECREF(py_code); + if ( PyGetError(&err) || py_result == NULL ) + { + warning("notify_when(): Error occured while notifying object.\n%s", err.c_str()); + ok = false; + } + Py_XDECREF(py_result); + } + in_notify = false; + + // Process any delayed notify_when() calls that + if ( !delayed_notify_when_list.empty() ) + { + for (notify_when_args_vec_t::iterator it = delayed_notify_when_list.begin(), it_end=delayed_notify_when_list.end(); + it != it_end; + ++it) + { + notify_when(it->when, it->py_callable); + } + delayed_notify_when_list.qclear(); + } + return ok; + } + + //------------------------------------------------------------------------ + pywraps_notify_when_t() + { + in_notify = false; + } +}; + +static pywraps_notify_when_t *g_nw = NULL; + +//------------------------------------------------------------------------ +// Initializes the notify_when mechanism +// (Normally called by IDAPython plugin.init()) +bool pywraps_nw_init() +{ + if ( g_nw != NULL ) + return true; + g_nw = new pywraps_notify_when_t(); + if ( g_nw->init() ) + return true; + // Things went bad, undo! + delete g_nw; + g_nw = NULL; + return false; +} + +//------------------------------------------------------------------------ +bool pywraps_nw_notify(int slot, ...) +{ + if ( g_nw == NULL ) + return false; + va_list va; + va_start(va, slot); + bool ok = g_nw->notify_va(slot, va); + va_end(va); + return ok; +} + +//------------------------------------------------------------------------ +// Deinitializes the notify_when mechanism +bool pywraps_nw_term() +{ + if ( g_nw == NULL ) + return true; + // If could not deinitialize then return w/o stopping nw + if ( !g_nw->deinit() ) + return false; + // Cleanup + delete g_nw; + g_nw = NULL; + return true; +} + + + + +// Use these macros to define script<->C fields +#define DEFINE_SCFIELDX(name, type, is_opt) { #name, type, qoffsetof(CUR_STRUC, name), is_opt } +#define DEFINE_SCFIELD(name, type) DEFINE_SCFIELDX(name, type, 0) +#define DEFINE_SCFIELD_OPT(name, type) DEFINE_SCFIELDX(name, type, 1) + +enum scfield_types_t +{ + // Numeric fields + FT_FIRST_NUM, + FT_INT, + FT_SIZET, + FT_SSIZET, + FT_NUM16, + FT_NUM32, + FT_LAST_NUM, + // String field + FT_STR, + FT_CHAR, + // Object fields + FT_ARR, + // Allocated array of strings + FT_STRARR, + // Allocated array of 16bit numbers + FT_NUM16ARR, + // Fixed size character array. The size must be passed in the definition + FT_CHRARR_STATIC, +}; + +struct scfld_t +{ + const char *field_name; + uint32 field_type; + size_t field_offs; + bool is_optional; +}; + +#define FT_VALUE_MASK 0xFFFF0000 +// Possible return values of conversion functions +#define FT_NOT_FOUND -1 +#define FT_BAD_TYPE -2 +#define FT_OK 1 + +//----------------------------------------------------------------------- +class pycvt_t +{ + struct attr_t + { + qstring str; + uint64 u64; + // User is responsible to release this attribute when done + PyObject *py_obj; + }; + + //----------------------------------------------------------------------- + static int get_attr( + PyObject *py_obj, + const char *attrname, + int ft, + attr_t &val) + { + PyObject *py_attr; + if ( (py_attr = PyObject_TryGetAttrString(py_obj, attrname)) == NULL ) + return FT_NOT_FOUND; + + int cvt = FT_OK; + if ( ft == FT_STR || ft == FT_CHAR && PyString_Check(py_attr) ) + val.str = PyString_AsString(py_attr); + else if ( (ft > FT_FIRST_NUM && ft < FT_LAST_NUM) && PyGetNumber(py_attr, &val.u64) ) + ; // nothing to be done + // A string array? + else if ( (ft == FT_STRARR || ft == FT_NUM16ARR || FT_CHRARR_STATIC ) + && (PyList_CheckExact(py_attr) || PyIsSequenceType(py_attr)) ) + { + // Return a reference to the attribute + val.py_obj = py_attr; + // Do not decrement the reference to this attribute + py_attr = NULL; + } + else + cvt = FT_BAD_TYPE; + Py_XDECREF(py_attr); + return cvt; + } + + //----------------------------------------------------------------------- + static int idaapi make_str_list_cb( + PyObject *py_item, + Py_ssize_t index, + void *ud) + { + if ( !PyString_Check(py_item) ) + return CIP_FAILED; + char **a = (char **)ud; + a[index] = qstrdup(PyString_AsString(py_item)); + return CIP_OK; + } + + //----------------------------------------------------------------------- + // Converts an IDC list of strings to a C string list + static Py_ssize_t str_list_to_str_arr( + PyObject *py_list, + char ***arr) + { + // Take the size + Py_ssize_t size = pyvar_walk_list(py_list); + + // Allocate a buffer + char **a = (char **)qalloc((size + 1) * sizeof(char *)); + + // Walk and populate + size = pyvar_walk_list(py_list, make_str_list_cb, a); + + // Make the list NULL terminated + a[size] = NULL; + + // Return the list to the user + *arr = a; + + // Return the size of items processed + return size; + } + + //----------------------------------------------------------------------- + typedef qvector uint64vec_t; + static int idaapi make_int_list( + PyObject *py_item, + Py_ssize_t index, + void *ud) + { + uint64 val; + if ( !PyGetNumber(py_item, &val) ) + return CIP_FAILED; + uint64vec_t *vec = (uint64vec_t *)ud; + vec->push_back(val); + return CIP_OK; + } + +public: + //----------------------------------------------------------------------- + // Frees a NULL terminated list of fields + static void free_fields( + const scfld_t *fields, + void *store_area) + { + for ( int i=0; ; i++ ) + { + // End of list? + const scfld_t &fd = fields[i]; + if ( fd.field_name == NULL ) + break; + + void *store = (void *)((char *)store_area + fd.field_offs); + int ft = fd.field_type & ~FT_VALUE_MASK; + switch ( ft ) + { + case FT_STR: // Simple string + { + char **s = (char **)store; + if ( *s != NULL ) + { + qfree(*s); + *s = NULL; + } + } + break; + + case FT_STRARR: // Array of strings + { + char ***op = (char ***)store, **p = *op; + while ( *p != NULL ) + qfree((void *)*p++); + qfree(*op); + *op = NULL; + } + break; + + case FT_NUM16ARR: + { + uint16 **arr = (uint16 **)store; + if ( *arr != NULL ) + { + qfree(*arr); + *arr = NULL; + } + } + break; + } + } + } + + //----------------------------------------------------------------------- + // Converts from a C structure to Python + static int from_c( + const scfld_t *fields, + void *read_area, + PyObject *py_obj) + { + PyObject *py_attr; + int i; + bool ok = false; + for ( i=0; ; i++ ) + { + // End of list? + const scfld_t &fd = fields[i]; + if ( fd.field_name == NULL ) + { + ok = true; + break; + } + + // Point to structure member + int ft = fd.field_type & ~FT_VALUE_MASK; + void *read = (void *)((char *)read_area + fd.field_offs); + // Create the python attribute properly + if ( ft > FT_FIRST_NUM && ft < FT_LAST_NUM ) + { + if ( ft == FT_NUM16 ) + py_attr = Py_BuildValue("H", *(uint16 *)read); + else if ( ft == FT_NUM32 ) + py_attr = Py_BuildValue("I", *(uint32 *)read); + else if ( ft == FT_INT ) + py_attr = Py_BuildValue("i", *(int *)read); + else if ( ft == FT_SIZET ) + py_attr = Py_BuildValue(PY_FMT64,*(size_t *)read); + else if ( ft == FT_SSIZET ) + py_attr = Py_BuildValue(PY_SFMT64,*(ssize_t *)read); + } + else if ( ft == FT_STR || ft == FT_CHAR ) + { + if ( ft == FT_STR ) + py_attr = PyString_FromString(*(char **)read); + else + py_attr = Py_BuildValue("c", *(char *)read); + } + else if ( ft == FT_STRARR ) + { + char **arr = *(char ***)read; + py_attr = PyList_New(0); + while ( *arr != NULL ) + PyList_Append(py_attr, PyString_FromString(*arr++)); + } + else + continue; + PyObject_SetAttrString(py_obj, fd.field_name, py_attr); + Py_XDECREF(py_attr); + } + return ok ? -1 : i; + } + + //----------------------------------------------------------------------- + // Converts fields from IDC and field description into a C structure + // If 'use_extlang' is specified, then the passed idc_obj is considered + // to be an opaque object and thus can be queried only through extlang + static int from_script( + const scfld_t *fields, + void *store_area, + PyObject *py_obj) + { + int i; + bool ok = false; + attr_t attr; + for ( i=0; ; i++ ) + { + // End of list? + const scfld_t &fd = fields[i]; + if ( fd.field_name == NULL ) + { + ok = true; + break; + } + + // Get field type + int ft = fd.field_type & ~FT_VALUE_MASK; + + // Point to structure member + void *store = (void *)((char *)store_area + fd.field_offs); + + // Retrieve attribute and type + int cvt = get_attr(py_obj, fd.field_name, ft, attr); + + // Attribute not found? + if ( cvt == FT_NOT_FOUND ) + { + // Skip optional fields + if ( fd.is_optional ) + continue; + break; + } + + if ( ft == FT_STR ) + *(char **)store = qstrdup(attr.str.c_str()); + else if ( ft == FT_NUM32 ) + *(uint32 *)store = uint32(attr.u64); + else if ( ft == FT_NUM16 ) + *(uint16 *)store = attr.u64 & 0xffff; + else if ( ft == FT_INT ) + *(int *)store = int(attr.u64); + else if ( ft == FT_SIZET ) + *(size_t *)store = size_t(attr.u64); + else if ( ft == FT_SSIZET ) + *(ssize_t *)store = ssize_t(attr.u64); + else if ( ft == FT_CHAR ) + *(char *)store = *attr.str.c_str(); + else if ( ft == FT_STRARR ) + { + str_list_to_str_arr(attr.py_obj, (char ***)store); + Py_DECREF(attr.py_obj); + } + else if ( ft == FT_CHRARR_STATIC ) + { + size_t sz = (fd.field_type & FT_VALUE_MASK) >> 16; + if ( sz == 0 ) + break; + uint64vec_t w; + char *a = (char *) store; + if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) ) + { + sz = qmin(w.size(), sz); + for ( size_t i=0; i < sz; i++ ) + a[i] = w[i] & 0xFF; + } + } + else if ( ft == FT_NUM16ARR ) + { + uint64vec_t w; + if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) > 0 ) + { + size_t max_sz = (fd.field_type & FT_VALUE_MASK) >> 16; + bool zero_term; + if ( max_sz == 0 ) + { + zero_term = true; + max_sz = w.size(); + } + else + { + zero_term = false; + max_sz = qmin(max_sz, w.size()); + } + // Allocate as much as we parsed elements + // Add one more element if list was zero terminated + uint16 *a = (uint16 *)qalloc(sizeof(uint16) * (max_sz + (zero_term ? 1 : 0))) ; + for ( size_t i=0; i < max_sz; i++ ) + a[i] = w[i] & 0xFF; + + if ( zero_term ) + a[max_sz] = 0; + *(uint16 **)store = a; + } + } + else + { + // Unsupported field type! + break; + } + } + return ok ? -1 : i; + } +}; + +// +%} + +// Do not create separate wrappers for default arguments +%feature("compactdefaultargs"); + +#ifdef __EA64__ +#ifdef __GNUC__ +%constant ea_t BADADDR = 0xFFFFFFFFFFFFFFFFll; +%constant sel_t BADSEL = 0xFFFFFFFFFFFFFFFFll; +%constant nodeidx_t BADNODE = 0xFFFFFFFFFFFFFFFFll; +#else // __GNUC__ +%constant ea_t BADADDR = 0xFFFFFFFFFFFFFFFFui64; +%constant sel_t BADSEL = 0xFFFFFFFFFFFFFFFFui64; +%constant nodeidx_t BADNODE = 0xFFFFFFFFFFFFFFFFui64; +#endif // __GNUC__ +#else //__EA64__ +%constant ea_t BADADDR = 0xFFFFFFFFL; +%constant sel_t BADSEL = 0xFFFFFFFFL; +%constant nodeidx_t BADNODE = 0xFFFFFFFFL; +#endif + +// Help SWIG to figure out the ulonglong type +#ifdef SWIGWIN +typedef unsigned __int64 ulonglong; +typedef __int64 longlong; +#else +typedef unsigned long long ulonglong; +typedef long long longlong; +#endif + +typedef int error_t; + +%include "typemaps.i" + +%include "cstring.i" +%include "carrays.i" +%include "cpointer.i" + +%include "typeconv.i" + +%pythoncode %{ +# +import struct +import traceback +import os +import sys +# ----------------------------------------------------------------------- + +# Seek constants +SEEK_SET = 0 # from the file start +SEEK_CUR = 1 # from the current position +SEEK_END = 2 # from the file end + +# Plugin constants +PLUGIN_MOD = 0x0001 +PLUGIN_DRAW = 0x0002 +PLUGIN_SEG = 0x0004 +PLUGIN_UNL = 0x0008 +PLUGIN_HIDE = 0x0010 +PLUGIN_DBG = 0x0020 +PLUGIN_PROC = 0x0040 +PLUGIN_FIX = 0x0080 +PLUGIN_SKIP = 0 +PLUGIN_OK = 1 +PLUGIN_KEEP = 2 + +# PyIdc conversion object IDs +PY_ICID_INT64 = 0 +"""int64 object""" +PY_ICID_BYREF = 1 +"""byref object""" +PY_ICID_OPAQUE = 2 +"""opaque object""" + +# ----------------------------------------------------------------------- +class pyidc_opaque_object_t(object): + """This is the base class for all Python<->IDC opaque objects""" + __idc_cvt_id__ = PY_ICID_OPAQUE + +# ----------------------------------------------------------------------- +class py_clinked_object_t(pyidc_opaque_object_t): + """This is a utility and base class for C linked objects""" + def __init__(self, lnk = None): + # static link: if a link was provided + self.__static_clink__ = True if lnk else False + + # Create link if it was not provided + self.__clink__ = lnk if lnk else self._create_clink() + + def __del__(self): + """Delete the link upon object destruction (only if not static)""" + if not self.__static_clink__: + self._del_clink(self.__clink__) + + def _create_clink(self): + """ + Overwrite me. + Creates a new clink + @return: PyCObject representing the C link + """ + pass + + def _del_clink(self, lnk): + """Overwrite me. + This method deletes the link + """ + pass + + def copy(self): + """Returns a new copy of this class""" + + # Create an unlinked instance + inst = self.__class__() + + # Assign self to the new instance + inst.assign(self) + + return inst + + def assign(self, other): + """ + Overwrite me. + This method allows you to assign an instance contents to anothers + @return: Boolean + """ + pass + + clink = property(lambda self: self.__clink__) + +# ----------------------------------------------------------------------- +class object_t(object): + """Helper class used to initialize empty objects""" + def __init__(self, **kwds): + self.__dict__ = kwds + + def __getitem__(self, idx): + """Allow access to object attributes by index (like dictionaries)""" + return getattr(self, idx) + +# ----------------------------------------------------------------------- +class plugin_t(pyidc_opaque_object_t): + """Base class for all scripted plugins.""" + pass + +# ----------------------------------------------------------------------- +class pyidc_cvt_helper__(object): + """ + This is a special helper object that helps detect which kind + of object is this python object wrapping and how to convert it + back and from IDC. + This object is characterized by its special attribute and its value + """ + def __init__(self, cvt_id, value): + self.__idc_cvt_id__ = cvt_id + self.value = value + + def __set_value(self, v): + self.__idc_cvt_value__ = v + def __get_value(self): + return self.__idc_cvt_value__ + value = property(__get_value, __set_value) + +# ----------------------------------------------------------------------- +class PyIdc_cvt_int64__(pyidc_cvt_helper__): + """Helper class for explicitly representing VT_INT64 values""" + + def __init__(self, v): + # id = 0 = int64 object + super(self.__class__, self).__init__(PY_ICID_INT64, v) + + # operation table + __op_table = \ + { + 0: lambda a, b: a + b, + 1: lambda a, b: a - b, + 2: lambda a, b: a * b, + 3: lambda a, b: a / b + } + # carries the operation given its number + def __op(self, op_n, other, rev=False): + a = self.value + # other operand of same type? then take its value field + if type(other) == type(self): + b = other.value + else: + b = other + if rev: + t = a + a = b + b = t + # construct a new object and return as the result + return self.__class__(self.__op_table[op_n](a, b)) + + # overloaded operators + def __add__(self, other): return self.__op(0, other) + def __sub__(self, other): return self.__op(1, other) + def __mul__(self, other): return self.__op(2, other) + def __div__(self, other): return self.__op(3, other) + def __radd__(self, other): return self.__op(0, other, True) + def __rsub__(self, other): return self.__op(1, other, True) + def __rmul__(self, other): return self.__op(2, other, True) + def __rdiv__(self, other): return self.__op(3, other, True) + +# ----------------------------------------------------------------------- +class PyIdc_cvt_refclass__(pyidc_cvt_helper__): + """Helper class for representing references to immutable objects""" + def __init__(self, v): + # id = one = byref object + super(self.__class__, self).__init__(PY_ICID_BYREF, v) + + def cstr(self): + """Returns the string as a C string (up to the zero termination)""" + return as_cstr(self.value) + +# ----------------------------------------------------------------------- +def as_cstr(val): + """ + Returns a C str from the passed value. The passed value can be of type refclass (returned by a call to buffer() or byref()) + It scans for the first \x00 and returns the string value up to that point. + """ + if isinstance(val, PyIdc_cvt_refclass__): + val = val.value + n = val.find('\0') + if n == -1: + return val + else: + return val[:n] + +# ----------------------------------------------------------------------- +def as_unicode(s): + """Convenience function to convert a string into appropriate unicode format""" + # use UTF16 big/little endian, depending on the environment? + return unicode(s).encode("UTF-16" + ("BE" if _idaapi.cvar.inf.mf else "LE")) + +# ----------------------------------------------------------------------- +def as_uint32(v): + """Returns a number as an unsigned int32 number""" + return v & 0xffffffff + +# ----------------------------------------------------------------------- +def as_int32(v): + """Returns a number as a signed int32 number""" + return -((~v & 0xffffffff)+1) + +# ----------------------------------------------------------------------- +def as_signed(v, nbits = 32): + """ + Returns a number as signed. The number of bits are specified by the user. + The MSB holds the sign. + """ + return -(( ~v & ((1 << nbits)-1) ) + 1) if v & (1 << nbits-1) else v + +# ---------------------------------------------------------------------- +def copy_bits(v, s, e=-1): + """ + Copy bits from a value + @param v: the value + @param s: starting bit + @param e: ending bit + """ + # end-bit not specified? use start bit (thus extract one bit) + if e == -1: + e = s + # swap start and end if start > end + if s > e: + e, s = s, e + + mask = 0 + for i in xrange(s, e+1): + mask |= 1 << i + + return (v & mask) >> s + +# ---------------------------------------------------------------------- +__struct_unpack_table = { + 1: ('b', 'B'), + 2: ('h', 'H'), + 4: ('l', 'L'), + 8: ('q', 'Q') +} + +# ---------------------------------------------------------------------- +def struct_unpack(buffer, signed = False, offs = 0): + """ + Unpack a buffer given its length and offset using struct.unpack_from(). + This function will know how to unpack the given buffer by using the lookup table '__struct_unpack_table' + If the buffer is of unknown length then None is returned. Otherwise the unpacked value is returned. + """ + # Supported length? + n = len(buffer) + if not n in __struct_unpack_table: + return None + # Conver to number + signed = 1 if signed else 0 + + # Unpack + return struct.unpack_from(__struct_unpack_table[n][signed], buffer, offs)[0] + +# ------------------------------------------------------------ +def IDAPython_ExecScript(script, g): + """ + Run the specified script. + It also addresses http://code.google.com/p/idapython/issues/detail?id=42 + + This function is used by the low-level plugin code. + """ + scriptpath = os.path.dirname(script) + if len(scriptpath) and scriptpath not in sys.path: + sys.path.append(scriptpath) + + # Save the argv, path, I/O and base modules for later cleanup + argv = sys.argv + path = sys.path + stdio = [sys.stdin, sys.stdout, sys.stderr] + basemodules = sys.modules.copy() + sys.argv = [ script ] + + # Adjust the __file__ path in the globals we pass to the script + old__file__ = g['__file__'] if '__file__' in g else '' + g['__file__'] = script + + PY_COMPILE_ERR = None + try: + execfile(script, g) + except Exception, e: + PY_COMPILE_ERR = str(e) + "\n" + traceback.format_exc() + finally: + # Restore the globals to the state before the script was run + g['__file__'] = old__file__ + + sys.argv = argv + sys.path = path + sys.stdin, sys.stdout, sys.stderr = stdio + + # Clean up the modules loaded by the script + for module in sys.modules.keys(): + if not module in basemodules: + del sys.modules[module] + + return PY_COMPILE_ERR + + + +# The general callback format of notify_when() is: +# def notify_when_callback(nw_code) +# In the case of NW_OPENIDB, the callback is: +# def notify_when_callback(nw_code, is_old_database) +NW_OPENIDB = 0x0001 +"""Notify when the database is opened. Its callback is of the form: def notify_when_callback(nw_code, is_old_database)""" +NW_CLOSEIDB = 0x0002 +"""Notify when the database is closed. Its callback is of the form: def notify_when_callback(nw_code)""" +NW_INITIDA = 0x0004 +"""Notify when the IDA starts. Its callback is of the form: def notify_when_callback(nw_code)""" +NW_TERMIDA = 0x0008 +"""Notify when the IDA terminates. Its callback is of the form: def notify_when_callback(nw_code)""" +NW_REMOVE = 0x0010 +"""Use this flag with other flags to uninstall a notifywhen callback""" + +# +%} + +%include "pro.i" + +// Do not move this. We need to override the define from pro.h +#define CASSERT(type) + +// Convert all of these +%cstring_output_maxstr_none(char *buf, size_t bufsize); +%binary_output_or_none(void *buf, size_t bufsize); +%binary_output_with_size(void *buf, size_t *bufsize); + +// Accept single Python string for const void * + size input arguments +// For example: put_many_bytes() and patch_many_bytes() +%apply (char *STRING, int LENGTH) { (const void *buf, size_t size) }; +%apply (char *STRING, int LENGTH) { (const void *buf, size_t len) }; +%apply (char *STRING, int LENGTH) { (const void *value, size_t length) }; +%apply (char *STRING, int LENGTH) { (const void *dataptr,size_t len) }; + +// Create wrapper classes for basic type arrays +%array_class(uchar, uchar_array); +%array_class(tid_t, tid_array); +%array_class(ea_t, ea_array); +%array_class(sel_t, sel_array); +%array_class(uval_t, uval_array); +%pointer_class(int, int_pointer); +%pointer_class(ea_t, ea_pointer); +%pointer_class(sval_t, sval_pointer); +%pointer_class(sel_t, sel_pointer); + +%include "ida.i" +%include "idd.i" +%include "idp.i" +%include "netnode.i" +%include "nalt.i" + +%include "allins.i" +%include "area.i" +%include "auto.i" +%include "bytes.i" +%include "dbg.i" +%include "diskio.i" +%include "entry.i" +%include "enum.i" +%include "expr.i" +%include "fixup.i" +%include "frame.i" +%include "funcs.i" + +%inline %{ + +// +//------------------------------------------------------------------------ +/* +# +def parse_command_line(cmdline): + """ + Parses a space separated string (quotes and escape character are supported) + @param cmdline: The command line to parse + @return: A list of strings or None on failure + """ + pass +# +*/ +static PyObject *py_parse_command_line(const char *cmdline) +{ + qstrvec_t args; + if ( parse_command_line(cmdline, &args) == 0 ) + Py_RETURN_NONE; + + PyObject *py_list = PyList_New(args.size()); + for ( size_t i=0; i +def get_inf_structure(): + """ + Returns the global variable 'inf' (an instance of idainfo structure, see ida.hpp) + """ + pass +# +*/ +idainfo *get_inf_structure(void) +{ + return &inf; +} + +//------------------------------------------------------------------------- +// Declarations from Python.cpp +/* +# +def set_script_timeout(timeout): + """ + Changes the script timeout value. + @param timeout: This value is in seconds. + If this value is set to zero then the script will never timeout. + @return: returns the old timeout value + """ + pass +# +*/ +int set_script_timeout(int timeout); +/* +# +def enable_extlang_python(enable): + """ + Enables or disables Python extlang. + When enabled, all expressions will be evaluated by Python. + @param enable: Set to True to enable, False otherwise + """ + pass +# +*/ +void enable_extlang_python(bool enable); +void enable_python_cli(bool enable); + +/* +# +def RunPythonStatement(stmt): + """ + This is an IDC function exported from the Python plugin. + It is used to evaluate Python statements from IDC. + @param stmt: The statement to evaluate + @return: 0 - on success otherwise a string containing the error + """ + pass +# +*/ + + + +//------------------------------------------------------------------------ +/* +# +def notify_when(when, callback): + """ + Register a callback that will be called when an event happens. + @param when: one of NW_XXXX constants + @param callback: This callback prototype varies depending on the 'when' parameter: + The general callback format: + def notify_when_callback(nw_code) + In the case of NW_OPENIDB: + def notify_when_callback(nw_code, is_old_database) + @return: Boolean + """ + pass +# +*/ +static bool notify_when(int when, PyObject *py_callable) +{ + if ( g_nw == NULL || !PyCallable_Check(py_callable) ) + return false; + return g_nw->notify_when(when, py_callable); +} + +// +%} + +%include "gdl.i" +%include "ints.i" +%include "kernwin.i" +%include "lines.i" +%include "loader.i" +%include "moves.i" +%include "name.i" +%include "offset.i" +%include "queue.i" +%include "search.i" +%include "segment.i" +%include "srarea.i" +%include "strlist.i" +%include "struct.i" +%include "typeinf.i" +%include "ua.i" +%include "xref.i" +#ifdef __NT__ + %include "graph.i" +#endif +%include "fpro.i" diff --git a/swig/idd.i b/swig/idd.i index 0faca86..c3092e9 100644 --- a/swig/idd.i +++ b/swig/idd.i @@ -2,6 +2,7 @@ %ignore memory_info_t; %ignore register_info_t; %ignore appcall; +%ignore gdecode_t; %apply unsigned char { char dtyp }; %include "idd.hpp" @@ -16,7 +17,7 @@ static bool dbg_can_query() { // Reject the request only if no debugger is set // or the debugger cannot be queried while not in suspended state - return !(dbg == NULL || (!dbg->may_disturb() && get_process_state() > DSTATE_SUSP)); + return dbg != NULL && (dbg->may_disturb() || get_process_state() < DSTATE_NOTASK); } //------------------------------------------------------------------------- @@ -42,116 +43,6 @@ static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas) return py_list; } -//------------------------------------------------------------------------- -PyObject *dbg_get_memory_info() -{ - if (!dbg_can_query()) - Py_RETURN_NONE; - - // Invalidate memory - invalidate_dbgmem_config(); - invalidate_dbgmem_contents(BADADDR, BADADDR); - - meminfo_vec_t areas; - dbg->get_memory_info(areas); - return meminfo_vec_t_to_py(areas); -} - -//------------------------------------------------------------------------- -PyObject *dbg_get_registers() -{ - if (dbg == NULL) - Py_RETURN_NONE; - - PyObject *py_list = PyList_New(dbg->registers_size); - - for (int i=0;iregisters_size;i++) - { - register_info_t &ri = dbg->registers[i]; - PyObject *py_bits; - - // Does this register have bit strings? - if (ri.bit_strings != NULL) - { - int nbits = (int)b2a_width((int)get_dtyp_size(ri.dtyp), 0) * 4; - py_bits = PyList_New(nbits); - for (int i=0;ithread_get_sreg_base(tid, sreg_value, &answer) != 1) - Py_RETURN_NONE; - return Py_BuildValue(PY_FMT64, pyul_t(answer)); -} - -//------------------------------------------------------------------------- -PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) -{ - uint64 ea, sz; - if ( !dbg_can_query() || !PyGetNumber(py_ea, &ea) || !PyGetNumber(py_sz, &sz) ) - Py_RETURN_NONE; - - char *buf = new char[size_t(sz)]; - if ( buf == NULL ) - Py_RETURN_NONE; - - PyObject *ret; - if ( (size_t)dbg->read_memory(ea_t(ea), buf, size_t(sz)) == sz ) - { - ret = PyString_FromStringAndSize(buf, (Py_ssize_t)sz); - } - else - { - Py_INCREF(Py_None); - ret = Py_None; - } - delete [] buf; - return ret; -} - -//------------------------------------------------------------------------- -PyObject *dbg_write_memory(PyObject *py_ea, PyObject *py_buf) -{ - uint64 ea; - if ( !dbg_can_query() || !PyString_Check(py_buf) || !PyGetNumber(py_ea, &ea) ) - Py_RETURN_NONE; - - size_t sz = PyString_GET_SIZE(py_buf); - void *buf = (void *)PyString_AS_STRING(py_buf); - if ( dbg->write_memory(ea_t(ea), buf, sz) != sz ) - Py_RETURN_FALSE; - Py_RETURN_TRUE; -} - //------------------------------------------------------------------------- PyObject *py_appcall( ea_t func_ea, @@ -202,7 +93,7 @@ PyObject *py_appcall( msg("input variables:\n" "----------------\n"); qstring s; - for (Py_ssize_t i=0;i -PyObject *dbg_write_memory(PyObject *py_ea, PyObject *py_buf); -PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz); -PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_value); -PyObject *dbg_get_registers(); -PyObject *dbg_get_memory_info(); + +//------------------------------------------------------------------------- +/* +# +def dbg_get_registers(): + """ + This function returns the register definition from the currently loaded debugger. + Basically, it returns an array of structure similar to to idd.hpp / register_info_t + @return: + None if no debugger is loaded + tuple(name, flags, class, dtyp, bit_strings, bit_strings_default_mask) + The bit_strings can be a tuple of strings or None (if the register does not have bit_strings) + """ + pass +# +*/ +static PyObject *dbg_get_registers() +{ + if ( dbg == NULL ) + Py_RETURN_NONE; + + PyObject *py_list = PyList_New(dbg->registers_size); + + for ( int i=0; iregisters_size; i++ ) + { + register_info_t &ri = dbg->registers[i]; + PyObject *py_bits; + + // Does this register have bit strings? + if ( ri.bit_strings != NULL ) + { + int nbits = (int)b2a_width((int)get_dtyp_size(ri.dtyp), 0) * 4; + py_bits = PyList_New(nbits); + for ( int i=0; i +def dbg_get_thread_sreg_base(tid, sreg_value): + """ + Returns the segment register base value + @param tid: thread id + @param sreg_value: segment register (selector) value + @return: + - The base as an 'ea' + - Or None on failure + """ + pass +# +*/ +static PyObject *dbg_get_thread_sreg_base(PyObject *py_tid, PyObject *py_sreg_value) +{ + if ( !dbg_can_query() || !PyInt_Check(py_tid) || !PyInt_Check(py_sreg_value) ) + Py_RETURN_NONE; + ea_t answer; + thid_t tid = PyInt_AsLong(py_tid); + int sreg_value = PyInt_AsLong(py_sreg_value); + if ( dbg->thread_get_sreg_base(tid, sreg_value, &answer) != 1 ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, pyul_t(answer)); +} + +//------------------------------------------------------------------------- +/* +# +def dbg_read_memory(ea, sz): + """ + Reads from the debugee's memory at the specified ea + @return: + - The read buffer (as a string) + - Or None on failure + """ + pass +# +*/ +static PyObject *dbg_read_memory(PyObject *py_ea, PyObject *py_sz) +{ + uint64 ea, sz; + if ( !dbg_can_query() || !PyGetNumber(py_ea, &ea) || !PyGetNumber(py_sz, &sz) ) + Py_RETURN_NONE; + + // Create a Python string + PyObject *ret = PyString_FromStringAndSize(NULL, Py_ssize_t(sz)); + if ( ret == NULL ) + Py_RETURN_NONE; + + // Get the internal buffer + Py_ssize_t len; + char *buf; + PyString_AsStringAndSize(ret, &buf, &len); + + if ( (size_t)dbg->read_memory(ea_t(ea), buf, size_t(sz)) != sz ) + { + // Release the string on failure + Py_DECREF(ret); + // Return None on failure + Py_RETURN_NONE; + } + return ret; +} + +//------------------------------------------------------------------------- +/* +# +def dbg_write_memory(ea, buffer): + """ + Writes a buffer to the debugee's memory + @return: Boolean + """ + pass +# +*/ +static PyObject *dbg_write_memory(PyObject *py_ea, PyObject *py_buf) +{ + uint64 ea; + if ( !dbg_can_query() || !PyString_Check(py_buf) || !PyGetNumber(py_ea, &ea) ) + Py_RETURN_NONE; + + size_t sz = PyString_GET_SIZE(py_buf); + void *buf = (void *)PyString_AS_STRING(py_buf); + if ( dbg->write_memory(ea_t(ea), buf, sz) != sz ) + Py_RETURN_FALSE; + Py_RETURN_TRUE; +} + +//------------------------------------------------------------------------- +/* +# +def dbg_get_name(): + """ + This function returns the current debugger's name. + @return: Debugger name or None if no debugger is active + """ + pass +# +*/ static PyObject *dbg_get_name() { if ( dbg == NULL ) @@ -305,6 +350,47 @@ static PyObject *dbg_get_name() return PyString_FromString(dbg->name); } +//------------------------------------------------------------------------- +/* +# +def dbg_get_memory_info(): + """ + This function returns the memory configuration of a debugged process. + @return: + None if no debugger is active + tuple(startEA, endEA, name, sclass, sbase, bitness, perm) + """ + pass +# +*/ +static PyObject *dbg_get_memory_info() +{ + if ( !dbg_can_query() ) + Py_RETURN_NONE; + + // Invalidate memory + invalidate_dbgmem_config(); + invalidate_dbgmem_contents(BADADDR, BADADDR); + + meminfo_vec_t areas; + dbg->get_memory_info(areas); + return meminfo_vec_t_to_py(areas); +} + +//------------------------------------------------------------------------- +/* +# +def dbg_can_query(): + """ + This function can be used to check if the debugger can be queried: + - debugger is loaded + - process is suspended + - process is not suspended but can take requests + @return: Boolean + """ + pass +# +*/ static bool dbg_can_query(); PyObject *py_appcall( ea_t func_ea, @@ -368,15 +454,16 @@ bool can_exc_continue(const debug_event_t* ev) import types # ----------------------------------------------------------------------- -# This class is used with |Appcall.array() method class Appcall_array__(object): + """This class is used with Appcall.array() method""" def __init__(self, tp): self.__type = tp def pack(self, L): + """Packs a list or tuple into a byref buffer""" t = type(L) if not (t == types.ListType or t == types.TupleType): - raise ValueError, "Either a list or a type must be passed" + raise ValueError, "Either a list or a tuple must be passed" self.__size = len(L) if self.__size == 1: self.__typedobj = Appcall__.typedobj(self.__type + ";") @@ -390,6 +477,7 @@ class Appcall_array__(object): return None def try_to_convert_to_list(self, obj): + """Is this object a list? We check for the existance of attribute zero and attribute self.size-1""" if not (hasattr(obj, "0") and hasattr(obj, str(self.__size-1))): return obj # at this point, we are sure we have an "idc list" @@ -397,6 +485,7 @@ class Appcall_array__(object): return [getattr(obj, str(x)) for x in xrange(0, self.__size)] def unpack(self, buf, as_list=True): + """Unpacks an array back into a list or an object""" # take the value from the special ref object if isinstance(buf, PyIdc_cvt_refclass__): buf = buf.value @@ -412,15 +501,6 @@ class Appcall_array__(object): return obj return self.try_to_convert_to_list(obj) -# ----------------------------------------------------------------------- -# This class is used with the obj() method -class Appcall_object__(object): - """Helper class used to initialize empty objects""" - def __init__(self, **kwds): - self.__dict__ = kwds - - def __getitem__(self, idx): - return getattr(self, idx) # ----------------------------------------------------------------------- # Wrapper class for the appcall() @@ -444,13 +524,29 @@ class Appcall_callable__(object): self.__type = tp self.__fields = fld self.__options = None # Appcall options + self.__timeout = None # Appcall timeout + + def __get_timeout(self): + return self.__timeout + def __set_timeout(self, v): + self.__timeout = v + timeout = property(__get_timeout, __set_timeout) + """An Appcall instance can change its timeout value with this attribute""" def __get_options(self): return self.__options if self.__options != None else Appcall__.get_appcall_options() + def __set_options(self, v): + if self.timeout: + # If timeout value is set, then put the timeout flag and encode the timeout value + v |= Appcall__.APPCALL_TIMEOUT | (self.timeout << 16) + else: + # Timeout is not set, then clear the timeout flag + v &= ~Appcall__.APPCALL_TIMEOUT self.__options = v - """Sets the Appcall options locally to this Appcall instance""" + options = property(__get_options, __set_options) + """Sets the Appcall options locally to this Appcall instance""" def __call__(self, *args): """Make object callable. We redirect execution to idaapi.appcall()""" @@ -475,7 +571,7 @@ class Appcall_callable__(object): arg_list) except Exception, e: e_obj = e - + # Restore appcall options Appcall__.set_appcall_options(old_opt) @@ -488,8 +584,9 @@ class Appcall_callable__(object): return self.__ea def __set_ea(self, val): self.__ea = val - """Returns or sets the EA associated with this object""" + ea = property(__get_ea, __set_ea) + """Returns or sets the EA associated with this object""" def __get_size(self): if self.__type == None: @@ -498,18 +595,20 @@ class Appcall_callable__(object): if not r: return -1 return r - """Returns the size of the type""" + size = property(__get_size) + """Returns the size of the type""" def __get_type(self): return self.__type - """Returns the typestring""" + type = property(__get_type) + """Returns the typestring""" def __get_fields(self): return self.__fields - """Returns the typestring""" fields = property(__get_fields) + """Returns the field names""" def retrieve(self, src=None, flags=0): """ @@ -531,8 +630,9 @@ class Appcall_callable__(object): """ Packs an object into a given ea if provided or into a string if no address was passed. - @return: - If packing to a string then a Tuple(Boolean, packed_string or error code) - - If packing to the database then a return code is returned (0 is success) + @return: + - If packing to a string then a Tuple(Boolean, packed_string or error code) + - If packing to the database then a return code is returned (0 is success) """ # no ea passed? thus pack to a string @@ -543,6 +643,8 @@ class Appcall_callable__(object): # ----------------------------------------------------------------------- class Appcall_consts__(object): + """Helper class used by Appcall.Consts attribute + It is used to retrieve constants via attribute access""" def __init__(self, default=0): self.__default = default @@ -551,26 +653,34 @@ class Appcall_consts__(object): # ----------------------------------------------------------------------- class Appcall__(object): + APPCALL_MANUAL = 0x1 """ Only set up the appcall, do not run it. you should call CleanupAppcall() when finished """ - APPCALL_MANUAL = 0x1 + + APPCALL_DEBEV = 0x2 """ Return debug event information If this bit is set, exceptions during appcall will generate idc exceptions with full information about the exception """ - APPCALL_DEBEV = 0x2 + + APPCALL_TIMEOUT = 0x4 + """ + Appcall with timeout + The timeout value in milliseconds is specified + in the high 2 bytes of the 'options' argument: + If timed out, errbuf will contain "timeout". + """ def __init__(self): self.__consts = Appcall_consts__() - def __get_consts(self): return self.__consts - """Use Appcall.Consts.CONST_NAME to access constants""" Consts = property(__get_consts) + """Use Appcall.Consts.CONST_NAME to access constants""" @staticmethod def __name_or_ea(name_or_ea): @@ -591,7 +701,7 @@ class Appcall__(object): @staticmethod def proto(name_or_ea, prototype, flags = None): - """Allows you to instantiate an appcall with the desired prototype""" + """Allows you to instantiate an appcall (callable object) with the desired prototype""" # resolve and raise exception on error ea = Appcall__.__name_or_ea(name_or_ea) @@ -605,7 +715,7 @@ class Appcall__(object): return Appcall_callable__(ea, result[1], result[2]) def __getattr__(self, name_or_ea): - """Allows you to call functions as if they were member functions""" + """Allows you to call functions as if they were member functions (by returning a callable object)""" # resolve and raise exception on error ea = self.__name_or_ea(name_or_ea) if ea == _idaapi.BADADDR: @@ -663,7 +773,7 @@ class Appcall__(object): @staticmethod def obj(**kwds): """Returns an empty object or objects with attributes as passed via its keywords arguments""" - return Appcall_object__(**kwds) + return object_t(**kwds) @staticmethod def cstr(val): @@ -695,12 +805,14 @@ class Appcall__(object): @staticmethod def set_appcall_options(opt): + """Method to change the Appcall options globally (not per Appcall)""" old_opt = Appcall__.get_appcall_options() _idaapi.cvar.inf.appcall_options = opt return old_opt @staticmethod def get_appcall_options(): + """Return the global Appcall options""" return _idaapi.cvar.inf.appcall_options @staticmethod diff --git a/swig/idp.i b/swig/idp.i index 540367f..424299c 100644 --- a/swig/idp.i +++ b/swig/idp.i @@ -1,339 +1,612 @@ -// Ignore the following symbols -%ignore WorkReg; -%ignore AbstractRegister; -%ignore rginfo; -%ignore insn_t::get_canon_mnem; -%ignore insn_t::get_canon_feature; -%ignore insn_t::is_canon_insn; -%ignore bytes_t; -%ignore IDPOPT_STR; -%ignore IDPOPT_NUM; -%ignore IDPOPT_BIT; -%ignore IDPOPT_FLT; -%ignore IDPOPT_I64; -%ignore IDPOPT_OK; -%ignore IDPOPT_BADKEY; -%ignore IDPOPT_BADTYPE; -%ignore IDPOPT_BADVALUE; -%ignore set_options_t; -%ignore read_user_config_file; - -%ignore s_preline; -%ignore ca_operation_t; -%ignore _chkarg_cmd; -%ignore ENUM_SIZE; - -%ignore asm_t::checkarg_dispatch; -%ignore asm_t::func_header; -%ignore asm_t::func_footer; -%ignore asm_t::get_type_name; -%ignore instruc_t; -%ignore processor_t; -%ignore ph; -%ignore IDB_Callback; - -%ignore free_processor_module; -%ignore read_config_file; - -%ignore gen_idb_event; - -%include "idp.hpp" - -%feature("director") IDB_Hooks; - -%inline %{ -int idaapi IDB_Callback(void *ud, int notification_code, va_list va); -class IDB_Hooks -{ -public: - virtual ~IDB_Hooks() {}; - - bool hook() { return hook_to_notification_point(HT_IDB, IDB_Callback, this); } - bool unhook() { return unhook_from_notification_point(HT_IDB, IDB_Callback, this); } - /* Hook functions to override in Python */ - virtual int byte_patched(ea_t ea) { return 0; }; - virtual int cmt_changed(ea_t, bool repeatable_cmt) { return 0; }; - virtual int ti_changed(ea_t ea, const type_t *type, const p_list *fnames) { msg("ti_changed hook not supported yet\n"); return 0; }; - virtual int op_ti_changed(ea_t ea, int n, const type_t *type, const p_list *fnames) { msg("op_ti_changed hook not supported yet\n"); return 0; }; - virtual int op_type_changed(ea_t ea, int n) { return 0; }; - virtual int enum_created(enum_t id) { return 0; }; - virtual int enum_deleted(enum_t id) { return 0; }; - virtual int enum_bf_changed(enum_t id) { return 0; }; - virtual int enum_renamed(enum_t id) { return 0; }; - virtual int enum_cmt_changed(enum_t id) { return 0; }; - virtual int enum_const_created(enum_t id, const_t cid) { return 0; }; - virtual int enum_const_deleted(enum_t id, const_t cid) { return 0; }; - virtual int struc_created(tid_t struc_id) { return 0; }; - virtual int struc_deleted(tid_t struc_id) { return 0; }; - virtual int struc_renamed(struc_t *sptr) { return 0; }; - virtual int struc_expanded(struc_t *sptr) { return 0; }; - virtual int struc_cmt_changed(tid_t struc_id) { return 0; }; - virtual int struc_member_created(struc_t *sptr, member_t *mptr) { return 0; }; - virtual int struc_member_deleted(struc_t *sptr, tid_t member_id) { return 0; }; - virtual int struc_member_renamed(struc_t *sptr, member_t *mptr) { return 0; }; - virtual int struc_member_changed(struc_t *sptr, member_t *mptr) { return 0; }; - virtual int thunk_func_created(func_t *pfn) { return 0; }; - virtual int func_tail_appended(func_t *pfn, func_t *tail) { return 0; }; - virtual int func_tail_removed(func_t *pfn, ea_t tail_ea) { return 0; }; - virtual int tail_owner_changed(func_t *tail, ea_t owner_func) { return 0; }; - virtual int func_noret_changed(func_t *pfn) { return 0; }; - virtual int segm_added(segment_t *s) { return 0; }; - virtual int segm_deleted(ea_t startEA) { return 0; }; - virtual int segm_start_changed(segment_t *s) { return 0; }; - virtual int segm_end_changed(segment_t *s) { return 0; }; - virtual int segm_moved(ea_t from, ea_t to, asize_t size) { return 0; }; -}; - -int idaapi IDB_Callback(void *ud, int notification_code, va_list va) -{ - class IDB_Hooks *proxy = (class IDB_Hooks *)ud; - ea_t ea, ea2; - bool repeatable_cmt; - /*type_t *type;*/ - /* p_list *fnames; */ - int n; - enum_t id; - const_t cid; - tid_t struc_id; - struc_t *sptr; - member_t *mptr; - tid_t member_id; - func_t *pfn; - func_t *tail; - segment_t *seg; - asize_t size; - - try { - switch (notification_code) - { - case idb_event::byte_patched: - ea = va_arg(va, ea_t); - return proxy->byte_patched(ea); - - case idb_event::cmt_changed: - ea = va_arg(va, ea_t); - repeatable_cmt = va_arg(va, int); - return proxy->cmt_changed(ea, repeatable_cmt); -#if 0 - case idb_event::ti_changed: - ea = va_arg(va, ea_t); - type = va_arg(va, type_t *); - fnames = va_arg(va, fnames); - return proxy->ti_changed(ea, type, fnames); - - case idb_event::op_ti_changed: - ea = va_arg(va, ea_t); - n = va_arg(va, int); - type = va_arg(va, type_t *); - fnames = va_arg(va, fnames); - return proxy->op_ti_changed(ea, n, type, fnames); -#endif - case idb_event::op_type_changed: - ea = va_arg(va, ea_t); - n = va_arg(va, int); - return proxy->op_type_changed(ea, n); - - case idb_event::enum_created: - id = va_arg(va, enum_t); - return proxy->enum_created(id); - - case idb_event::enum_deleted: - id = va_arg(va, enum_t); - return proxy->enum_deleted(id); - - case idb_event::enum_bf_changed: - id = va_arg(va, enum_t); - return proxy->enum_bf_changed(id); - - case idb_event::enum_cmt_changed: - id = va_arg(va, enum_t); - return proxy->enum_cmt_changed(id); - - case idb_event::enum_const_created: - id = va_arg(va, enum_t); - cid = va_arg(va, const_t); - return proxy->enum_const_created(id, cid); - - case idb_event::enum_const_deleted: - id = va_arg(va, enum_t); - cid = va_arg(va, const_t); - return proxy->enum_const_deleted(id, cid); - - case idb_event::struc_created: - struc_id = va_arg(va, tid_t); - return proxy->struc_created(struc_id); - - case idb_event::struc_deleted: - struc_id = va_arg(va, tid_t); - return proxy->struc_deleted(struc_id); - - case idb_event::struc_renamed: - sptr = va_arg(va, struc_t *); - return proxy->struc_renamed(sptr); - - case idb_event::struc_expanded: - sptr = va_arg(va, struc_t *); - return proxy->struc_expanded(sptr); - - case idb_event::struc_cmt_changed: - struc_id = va_arg(va, tid_t); - return proxy->struc_cmt_changed(struc_id); - - case idb_event::struc_member_created: - sptr = va_arg(va, struc_t *); - mptr = va_arg(va, member_t *); - return proxy->struc_member_created(sptr, mptr); - - case idb_event::struc_member_deleted: - sptr = va_arg(va, struc_t *); - member_id = va_arg(va, tid_t); - return proxy->struc_member_deleted(sptr, member_id); - - case idb_event::struc_member_renamed: - sptr = va_arg(va, struc_t *); - mptr = va_arg(va, member_t *); - return proxy->struc_member_renamed(sptr, mptr); - - case idb_event::struc_member_changed: - sptr = va_arg(va, struc_t *); - mptr = va_arg(va, member_t *); - return proxy->struc_member_changed(sptr, mptr); - - case idb_event::thunk_func_created: - pfn = va_arg(va, func_t *); - return proxy->thunk_func_created(pfn); - - case idb_event::func_tail_appended: - pfn = va_arg(va, func_t *); - tail = va_arg(va, func_t *); - return proxy->func_tail_appended(pfn, tail); - - case idb_event::func_tail_removed: - pfn = va_arg(va, func_t *); - ea = va_arg(va, ea_t); - return proxy->func_tail_removed(pfn, ea); - - case idb_event::tail_owner_changed: - tail = va_arg(va, func_t *); - ea = va_arg(va, ea_t); - return proxy->tail_owner_changed(tail, ea); - - case idb_event::func_noret_changed: - pfn = va_arg(va, func_t *); - return proxy->func_noret_changed(pfn); - - case idb_event::segm_added: - seg = va_arg(va, segment_t *); - return proxy->segm_added(seg); - - case idb_event::segm_deleted: - ea = va_arg(va, ea_t); - return proxy->segm_deleted(ea); - - case idb_event::segm_start_changed: - seg = va_arg(va, segment_t *); - return proxy->segm_start_changed(seg); - - case idb_event::segm_end_changed: - seg = va_arg(va, segment_t *); - return proxy->segm_end_changed(seg); - - case idb_event::segm_moved: - ea = va_arg(va, ea_t); - ea2 = va_arg(va, ea_t); - size = va_arg(va, asize_t); - return proxy->segm_moved(ea, ea2, size); - } - } - catch (Swig::DirectorException &) - { - msg("Exception in IDP Hook function:\n"); - if (PyErr_Occurred()) - { - PyErr_Print(); - } - } - return 0; -} - -// Assemble an instruction into the database (display a warning if an error is found) -// args: -// ea_t ea - linear address of instruction -// ea_t cs - cs of instruction -// ea_t ip - ip of instruction -// bool use32 - is 32bit segment? -// const char *line - line to assemble -// returns: 1: success, 0: failure -inline const int assemble(ea_t ea, ea_t cs, ea_t ip, bool use32, const char *line) -{ - int inslen; - char buf[MAXSTR]; - - if (ph.notify != NULL) - { - inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf); - if (inslen > 0) - { - patch_many_bytes(ea, buf, inslen); - return 1; - } - } - return 0; -} - -// -//------------------------------------------------------------------------- - -//------------------------------------------------------------------------- -// Assemble an instruction to a buffer (display a warning if an error is found) -// args: -// ea_t ea - linear address of instruction -// ea_t cs - cs of instruction -// ea_t ip - ip of instruction -// bool use32 - is 32bit segment? -// const char *line - line to assemble -// returns: 1: success, 0: failure -static PyObject *AssembleLine(ea_t ea, ea_t cs, ea_t ip, bool use32, const char *line) -{ - int inslen; - char buf[MAXSTR]; - if (ph.notify != NULL && - (inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf)) > 0) - { - return PyString_FromStringAndSize(buf, inslen); - } - Py_RETURN_NONE; -} - -//------------------------------------------------------------------------- -static size_t ph_get_tbyte_size() -{ - return ph.tbyte_size; -} - -//------------------------------------------------------------------------- -static PyObject *ph_get_instruc() -{ - Py_ssize_t i = 0; - PyObject *py_result = PyTuple_New(ph.instruc_end - ph.instruc_start); - for ( instruc_t *p = ph.instruc + ph.instruc_start, *end = ph.instruc + ph.instruc_end; - p != end; - ++p ) - { - PyTuple_SetItem(py_result, i++, Py_BuildValue("(sI)", p->name, p->feature)); - } - return py_result; -} - -//------------------------------------------------------------------------- -static PyObject *ph_get_regnames() -{ - Py_ssize_t i = 0; - PyObject *py_result = PyList_New(ph.regsNum); - for ( Py_ssize_t i=0; i -%} +// Ignore the following symbols +%ignore WorkReg; +%ignore AbstractRegister; +%ignore rginfo; +%ignore insn_t::get_canon_mnem; +%ignore insn_t::get_canon_feature; +%ignore insn_t::is_canon_insn; +%ignore bytes_t; +%ignore IDPOPT_STR; +%ignore IDPOPT_NUM; +%ignore IDPOPT_BIT; +%ignore IDPOPT_FLT; +%ignore IDPOPT_I64; +%ignore IDPOPT_OK; +%ignore IDPOPT_BADKEY; +%ignore IDPOPT_BADTYPE; +%ignore IDPOPT_BADVALUE; +%ignore set_options_t; +%ignore read_user_config_file; + +%ignore s_preline; +%ignore ca_operation_t; +%ignore _chkarg_cmd; +%ignore ENUM_SIZE; + +%ignore asm_t::checkarg_dispatch; +%ignore asm_t::func_header; +%ignore asm_t::func_footer; +%ignore asm_t::get_type_name; +%ignore instruc_t; +%ignore processor_t; +%ignore ph; +%ignore IDB_Callback; +%ignore IDP_Callback; + +%ignore free_processor_module; +%ignore read_config_file; + +%ignore gen_idb_event; + +%include "idp.hpp" +%feature("director") IDB_Hooks; +%feature("director") IDP_Hooks; +%inline %{ +int idaapi IDB_Callback(void *ud, int notification_code, va_list va); +class IDB_Hooks +{ +public: + virtual ~IDB_Hooks() {}; + + bool hook() { return hook_to_notification_point(HT_IDB, IDB_Callback, this); } + bool unhook() { return unhook_from_notification_point(HT_IDB, IDB_Callback, this); } + /* Hook functions to override in Python */ + virtual int byte_patched(ea_t ea) { return 0; }; + virtual int cmt_changed(ea_t, bool repeatable_cmt) { return 0; }; + virtual int ti_changed(ea_t ea, const type_t *type, const p_list *fnames) { msg("ti_changed hook not supported yet\n"); return 0; }; + virtual int op_ti_changed(ea_t ea, int n, const type_t *type, const p_list *fnames) { msg("op_ti_changed hook not supported yet\n"); return 0; }; + virtual int op_type_changed(ea_t ea, int n) { return 0; }; + virtual int enum_created(enum_t id) { return 0; }; + virtual int enum_deleted(enum_t id) { return 0; }; + virtual int enum_bf_changed(enum_t id) { return 0; }; + virtual int enum_renamed(enum_t id) { return 0; }; + virtual int enum_cmt_changed(enum_t id) { return 0; }; + virtual int enum_member_created(enum_t id, const_t cid) { return 0; }; + virtual int enum_member_deleted(enum_t id, const_t cid) { return 0; }; + virtual int struc_created(tid_t struc_id) { return 0; }; + virtual int struc_deleted(tid_t struc_id) { return 0; }; + virtual int struc_renamed(struc_t *sptr) { return 0; }; + virtual int struc_expanded(struc_t *sptr) { return 0; }; + virtual int struc_cmt_changed(tid_t struc_id) { return 0; }; + virtual int struc_member_created(struc_t *sptr, member_t *mptr) { return 0; }; + virtual int struc_member_deleted(struc_t *sptr, tid_t member_id) { return 0; }; + virtual int struc_member_renamed(struc_t *sptr, member_t *mptr) { return 0; }; + virtual int struc_member_changed(struc_t *sptr, member_t *mptr) { return 0; }; + virtual int thunk_func_created(func_t *pfn) { return 0; }; + virtual int func_tail_appended(func_t *pfn, func_t *tail) { return 0; }; + virtual int func_tail_removed(func_t *pfn, ea_t tail_ea) { return 0; }; + virtual int tail_owner_changed(func_t *tail, ea_t owner_func) { return 0; }; + virtual int func_noret_changed(func_t *pfn) { return 0; }; + virtual int segm_added(segment_t *s) { return 0; }; + virtual int segm_deleted(ea_t startEA) { return 0; }; + virtual int segm_start_changed(segment_t *s) { return 0; }; + virtual int segm_end_changed(segment_t *s) { return 0; }; + virtual int segm_moved(ea_t from, ea_t to, asize_t size) { return 0; }; +}; + +// Assemble an instruction into the database (display a warning if an error is found) +// args: +// ea_t ea - linear address of instruction +// ea_t cs - cs of instruction +// ea_t ip - ip of instruction +// bool use32 - is 32bit segment? +// const char *line - line to assemble +// returns: 1: success, 0: failure +inline const int assemble(ea_t ea, ea_t cs, ea_t ip, bool use32, const char *line) +{ + int inslen; + char buf[MAXSTR]; + + if (ph.notify != NULL) + { + inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf); + if (inslen > 0) + { + patch_many_bytes(ea, buf, inslen); + return 1; + } + } + return 0; +} + +// +//------------------------------------------------------------------------- + +//------------------------------------------------------------------------- +/* +# +def AssembleLine(ea, cs, ip, use32, line): + """ + Assemble an instruction to a buffer (display a warning if an error is found) + + @param ea: linear address of instruction + @param cs: cs of instruction + @param ip: ip of instruction + @param use32: is 32bit segment + @param line: line to assemble + @return: + - None on failure + - or a string containing the assembled instruction + """ + pass +# +*/ +static PyObject *AssembleLine(ea_t ea, ea_t cs, ea_t ip, bool use32, const char *line) +{ + int inslen; + char buf[MAXSTR]; + if (ph.notify != NULL && + (inslen = ph.notify(ph.assemble, ea, cs, ip, use32, line, buf)) > 0) + { + return PyString_FromStringAndSize(buf, inslen); + } + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_tbyte_size(): + """ + Returns the 'ph.tbyte_size' field as defined in he processor module + """ + pass +# +*/ +static size_t ph_get_tbyte_size() +{ + return ph.tbyte_size; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_id(): + """ + Returns the 'ph.id' field + """ + pass +# +*/ +static size_t ph_get_id() +{ + return ph.id; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_instruc(): + """ + Returns a list of tuples (instruction_name, instruction_feature) containing the + instructions list as defined in he processor module + """ + pass +# +*/ +static PyObject *ph_get_instruc() +{ + Py_ssize_t i = 0; + PyObject *py_result = PyTuple_New(ph.instruc_end - ph.instruc_start); + for ( instruc_t *p = ph.instruc + ph.instruc_start, *end = ph.instruc + ph.instruc_end; + p != end; + ++p ) + { + PyTuple_SetItem(py_result, i++, Py_BuildValue("(sI)", p->name, p->feature)); + } + return py_result; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_regnames(): + """ + Returns the list of register names as defined in the processor module + """ + pass +# +*/ +static PyObject *ph_get_regnames() +{ + Py_ssize_t i = 0; + PyObject *py_result = PyList_New(ph.regsNum); + for ( Py_ssize_t i=0; i +class IDP_Hooks(object): + def custom_ana(self): + """ + Analyzes and decodes an instruction at idaapi.cmd.ea + - cmd.itype must be set >= idaapi.CUSTOM_CMD_ITYPE + - cmd.size must be set to the instruction length + + @return: Boolean + - False if the instruction is not recognized + - True if the instruction was decoded. idaapi.cmd should be filled in that case. + """ + pass + + def custom_out(self): + """ + Outputs the instruction defined in idaapi.cmd + + @return: Boolean (whether this instruction can be outputted or not) + """ + pass + + def custom_emu(self): + """ + Emulate instruction, create cross-references, plan to analyze + subsequent instructions, modify flags etc. Upon entrance to this function + all information about the instruction is in 'cmd' structure. + + @return: Boolean (whether this instruction has been emulated or not) + """ + pass + + def custom_outop(self, op): + """ + Notification to generate operand text. + If False was returned, then the standard operand output function will be called. + + The output buffer is inited with init_output_buffer() + and this notification may use out_...() functions to form the operand text + + @return: Boolean (whether the operand has been outputted or not) + """ + + def custom_mnem(self): + """ + Prints the mnemonic of the instruction defined in idaapi.cmd + + @return: + - None: No mnemonic. IDA will use the default mnemonic value if present + - String: The desired mnemonic string + """ + + def is_sane_insn(self, no_crefs): + """ + is the instruction sane for the current file type? + @param no_crefs: + - 1: the instruction has no code refs to it. + ida just tries to convert unexplored bytes + to an instruction (but there is no other + reason to convert them into an instruction) + - 0: the instruction is created because + of some coderef, user request or another + weighty reason. + @return: 1-ok, <=0-no, the instruction isn't likely to appear in the program + """ + pass + + def is_sane_insn(self, no_crefs): + """ + can a function start here? + @param state: autoanalysis phase + 0: creating functions + 1: creating chunks + + @return: integer (probability 0..100) + """ + pass +# +*/ +int idaapi IDP_Callback(void *ud, int notification_code, va_list va); +class IDP_Hooks +{ +public: + virtual ~IDP_Hooks() + { + } + + bool hook() + { + return hook_to_notification_point(HT_IDP, IDP_Callback, this); + } + + bool unhook() + { + return unhook_from_notification_point(HT_IDP, IDP_Callback, this); + } + + virtual bool custom_ana() + { + return false; + } + + virtual bool custom_out() + { + return false; + } + + virtual bool custom_emu() + { + return false; + } + + virtual bool custom_outop(PyObject *py_op) + { + return false; + } + + virtual PyObject *custom_mnem() + { + return NULL; + } + + virtual int is_sane_insn(int no_crefs) + { + return 0; + } + + virtual int may_be_func(int state) + { + return 0; + } +}; + +// +%} + +%{ +int idaapi IDB_Callback(void *ud, int notification_code, va_list va) +{ + class IDB_Hooks *proxy = (class IDB_Hooks *)ud; + ea_t ea, ea2; + bool repeatable_cmt; + /*type_t *type;*/ + /* p_list *fnames; */ + int n; + enum_t id; + const_t cid; + tid_t struc_id; + struc_t *sptr; + member_t *mptr; + tid_t member_id; + func_t *pfn; + func_t *tail; + segment_t *seg; + asize_t size; + + try { + switch (notification_code) + { + case idb_event::byte_patched: + ea = va_arg(va, ea_t); + return proxy->byte_patched(ea); + + case idb_event::cmt_changed: + ea = va_arg(va, ea_t); + repeatable_cmt = va_arg(va, int); + return proxy->cmt_changed(ea, repeatable_cmt); +#if 0 + case idb_event::ti_changed: + ea = va_arg(va, ea_t); + type = va_arg(va, type_t *); + fnames = va_arg(va, fnames); + return proxy->ti_changed(ea, type, fnames); + + case idb_event::op_ti_changed: + ea = va_arg(va, ea_t); + n = va_arg(va, int); + type = va_arg(va, type_t *); + fnames = va_arg(va, fnames); + return proxy->op_ti_changed(ea, n, type, fnames); +#endif + case idb_event::op_type_changed: + ea = va_arg(va, ea_t); + n = va_arg(va, int); + return proxy->op_type_changed(ea, n); + + case idb_event::enum_created: + id = va_arg(va, enum_t); + return proxy->enum_created(id); + + case idb_event::enum_deleted: + id = va_arg(va, enum_t); + return proxy->enum_deleted(id); + + case idb_event::enum_bf_changed: + id = va_arg(va, enum_t); + return proxy->enum_bf_changed(id); + + case idb_event::enum_cmt_changed: + id = va_arg(va, enum_t); + return proxy->enum_cmt_changed(id); + + case idb_event::enum_member_created: + id = va_arg(va, enum_t); + cid = va_arg(va, const_t); + return proxy->enum_member_created(id, cid); + + case idb_event::enum_member_deleted: + id = va_arg(va, enum_t); + cid = va_arg(va, const_t); + return proxy->enum_member_deleted(id, cid); + + case idb_event::struc_created: + struc_id = va_arg(va, tid_t); + return proxy->struc_created(struc_id); + + case idb_event::struc_deleted: + struc_id = va_arg(va, tid_t); + return proxy->struc_deleted(struc_id); + + case idb_event::struc_renamed: + sptr = va_arg(va, struc_t *); + return proxy->struc_renamed(sptr); + + case idb_event::struc_expanded: + sptr = va_arg(va, struc_t *); + return proxy->struc_expanded(sptr); + + case idb_event::struc_cmt_changed: + struc_id = va_arg(va, tid_t); + return proxy->struc_cmt_changed(struc_id); + + case idb_event::struc_member_created: + sptr = va_arg(va, struc_t *); + mptr = va_arg(va, member_t *); + return proxy->struc_member_created(sptr, mptr); + + case idb_event::struc_member_deleted: + sptr = va_arg(va, struc_t *); + member_id = va_arg(va, tid_t); + return proxy->struc_member_deleted(sptr, member_id); + + case idb_event::struc_member_renamed: + sptr = va_arg(va, struc_t *); + mptr = va_arg(va, member_t *); + return proxy->struc_member_renamed(sptr, mptr); + + case idb_event::struc_member_changed: + sptr = va_arg(va, struc_t *); + mptr = va_arg(va, member_t *); + return proxy->struc_member_changed(sptr, mptr); + + case idb_event::thunk_func_created: + pfn = va_arg(va, func_t *); + return proxy->thunk_func_created(pfn); + + case idb_event::func_tail_appended: + pfn = va_arg(va, func_t *); + tail = va_arg(va, func_t *); + return proxy->func_tail_appended(pfn, tail); + + case idb_event::func_tail_removed: + pfn = va_arg(va, func_t *); + ea = va_arg(va, ea_t); + return proxy->func_tail_removed(pfn, ea); + + case idb_event::tail_owner_changed: + tail = va_arg(va, func_t *); + ea = va_arg(va, ea_t); + return proxy->tail_owner_changed(tail, ea); + + case idb_event::func_noret_changed: + pfn = va_arg(va, func_t *); + return proxy->func_noret_changed(pfn); + + case idb_event::segm_added: + seg = va_arg(va, segment_t *); + return proxy->segm_added(seg); + + case idb_event::segm_deleted: + ea = va_arg(va, ea_t); + return proxy->segm_deleted(ea); + + case idb_event::segm_start_changed: + seg = va_arg(va, segment_t *); + return proxy->segm_start_changed(seg); + + case idb_event::segm_end_changed: + seg = va_arg(va, segment_t *); + return proxy->segm_end_changed(seg); + + case idb_event::segm_moved: + ea = va_arg(va, ea_t); + ea2 = va_arg(va, ea_t); + size = va_arg(va, asize_t); + return proxy->segm_moved(ea, ea2, size); + } + } + catch (Swig::DirectorException &) + { + msg("Exception in IDP Hook function:\n"); + if (PyErr_Occurred()) + { + PyErr_Print(); + } + } + return 0; +} + +// +//------------------------------------------------------------------------- +int idaapi IDP_Callback(void *ud, int notification_code, va_list va) +{ + IDP_Hooks *proxy = (IDP_Hooks *)ud; + int ret; + try + { + switch ( notification_code ) + { + default: + ret = 0; + break; + + case processor_t::custom_ana: + ret = proxy->custom_ana() ? 1 + cmd.size : 0; + break; + + case processor_t::custom_out: + ret = proxy->custom_out() ? 2 : 0; + break; + + case processor_t::custom_emu: + ret = proxy->custom_emu() ? 2 : 0; + break; + + case processor_t::custom_outop: + { + op_t *op = va_arg(va, op_t *); + PyObject *py_obj = create_idaapi_linked_class_instance(S_PY_OP_T_CLSNAME, op); + if ( py_obj == NULL ) + break; + ret = proxy->custom_outop(py_obj) ? 2 : 0; + Py_XDECREF(py_obj); + break; + } + + case processor_t::custom_mnem: + { + PyObject *py_ret = proxy->custom_mnem(); + if ( py_ret != NULL && PyString_Check(py_ret) ) + { + char *outbuffer = va_arg(va, char *); + size_t bufsize = va_arg(va, size_t); + + qstrncpy(outbuffer, PyString_AS_STRING(py_ret), bufsize); + ret = 2; + } + else + { + ret = 0; + } + Py_XDECREF(py_ret); + break; + } + + case processor_t::is_sane_insn: + { + int no_crefs = va_arg(va, int); + ret = proxy->is_sane_insn(no_crefs); + break; + } + + case processor_t::may_be_func: + { + int state = va_arg(va, int); + ret = proxy->may_be_func(state); + break; + } + } + } + catch (Swig::DirectorException &) + { + msg("Exception in IDP Hook function:\n"); + if ( PyErr_Occurred() ) + PyErr_Print(); + } + return ret; +} + +//------------------------------------------------------------------------- +// +%} \ No newline at end of file diff --git a/swig/kernwin.i b/swig/kernwin.i index 1f90b8d..26f62dd 100644 --- a/swig/kernwin.i +++ b/swig/kernwin.i @@ -1,2198 +1,2534 @@ -// Ignore the va_list functions -%ignore AskUsingForm_cv; -%ignore close_form; -%ignore vaskstr; -%ignore vasktext; -%ignore add_menu_item; -%ignore vwarning; -%ignore vinfo; -%ignore vnomem; -%ignore vmsg; -%ignore show_wait_box_v; -%ignore askbuttons_cv; -%ignore askfile_cv; -%ignore askyn_cv; -%ignore askyn_v; -// Ignore these string functions. There are trivial replacements in Python. -%ignore addblanks; -%ignore trim; -%ignore skipSpaces; -%ignore stristr; - -// Ignore the cli_t class -%ignore cli_t; - -%include "typemaps.i" - -// Make askaddr(), askseg(), and asklong() return a -// tuple: (result, value) -%apply unsigned long *INOUT { sval_t *value }; -%rename (_asklong) asklong; -%apply unsigned long *INOUT { ea_t *addr }; -%rename (_askaddr) askaddr; -%apply unsigned long *INOUT { sel_t *sel }; -%rename (_askseg) askseg; - -%inline %{ -void refresh_lists(void) -{ - callui(ui_list); -} -%} - -%pythoncode %{ -def asklong(defval, format): - res, val = _idaapi._asklong(defval, format) - - if res == 1: - return val - else: - return None - -def askaddr(defval, format): - res, ea = _idaapi._askaddr(defval, format) - - if res == 1: - return ea - else: - return None - -def askseg(defval, format): - res, sel = _idaapi._askseg(defval, format) - - if res == 1: - return sel - else: - return None - -%} - -# This is for get_cursor() -%apply int *OUTPUT {int *x, int *y}; - -# This is for read_selection() -%apply unsigned long *OUTPUT { ea_t *ea1, ea_t *ea2 }; - -%{ - -// -#ifdef __NT__ -//--------------------------------------------------------------------------- -// Base class for all custviewer place_t providers -class custviewer_data_t -{ -public: - virtual void *get_ud() = 0; - virtual place_t *get_min() = 0; - virtual place_t *get_max() = 0; -}; - -//--------------------------------------------------------------------------- -class cvdata_simpleline_t: public custviewer_data_t -{ -private: - strvec_t lines; - simpleline_place_t pl_min, pl_max; -public: - void *get_ud() - { - return &lines; - } - place_t *get_min() - { - return &pl_min; - } - place_t *get_max() - { - return &pl_max; - } - strvec_t &get_lines() - { - return lines; - } - void set_minmax(size_t start=size_t(-1), size_t end=size_t(-1)) - { - if ( start == size_t(-1) && end == size_t(-1) ) - { - end = lines.size(); - pl_min.n = 0; - pl_max.n = end == 0 ? 0 : end - 1; - } - else - { - pl_min.n = start; - pl_max.n = end; - } - } - bool set_line(size_t nline, simpleline_t &sl) - { - if ( nline >= lines.size() ) - return false; - lines[nline] = sl; - return true; - } - bool del_line(size_t nline) - { - if ( nline >= lines.size() ) - return false; - lines.erase(lines.begin()+nline); - return true; - } - void add_line(simpleline_t &line) - { - lines.push_back(line); - } - void add_line(const char *str) - { - lines.push_back(simpleline_t(str)); - } - bool insert_line(size_t nline, simpleline_t &line) - { - if ( nline >= lines.size() ) - return false; - lines.insert(lines.begin()+nline, line); - return true; - } - bool patch_line(size_t nline, size_t offs, int value) - { - if ( nline >= lines.size() ) - return false; - qstring &L = lines[nline].line; - L[offs] = (uchar) value & 0xFF; - return true; - } - const size_t to_lineno(place_t *pl) const - { - return ((simpleline_place_t *)pl)->n; - } - bool curline(place_t *pl, size_t *n) - { - if ( pl == NULL ) - return false; - *n = to_lineno(pl); - return true; - } - simpleline_t *get_line(size_t nline) - { - return nline >= lines.size() ? NULL : &lines[nline]; - } - simpleline_t *get_line(place_t *pl) - { - return pl == NULL ? NULL : get_line(((simpleline_place_t *)pl)->n); - } - const size_t count() const - { - return lines.size(); - } - void clear_lines() - { - lines.clear(); - set_minmax(); - } -}; - -//--------------------------------------------------------------------------- -class customviewer_t -{ -protected: - qstring _title; - TForm *_form; - TCustomControl *_cv; - custviewer_data_t *_data; - int _features; - enum - { - HAVE_HINT = 0x0001, - HAVE_KEYDOWN = 0x0002, - HAVE_POPUP = 0x0004, - HAVE_DBLCLICK = 0x0008, - HAVE_CURPOS = 0x0010, - HAVE_CLICK = 0x0020, - HAVE_CLOSE = 0x0040 - }; -private: - struct pyw_popupctx_t - { - size_t menu_id; - customviewer_t *cv; - pyw_popupctx_t(): menu_id(0), cv(NULL) { } - pyw_popupctx_t(size_t mid, customviewer_t *v): menu_id(mid), cv(v) { } - }; - typedef std::map pyw_popupmap_t; - static pyw_popupmap_t _global_popup_map; - static size_t _global_popup_id; - qstring _curline; - intvec_t _installed_popups; - - static bool idaapi s_popup_cb(void *ud) - { - customviewer_t *_this = (customviewer_t *)ud; - return _this->on_popup(); - } - - static bool idaapi s_popup_menu_cb(void *ud) - { - size_t mid = (size_t)ud; - pyw_popupmap_t::iterator it = _global_popup_map.find(mid); - if ( it == _global_popup_map.end() ) - return false; - return it->second.cv->on_popup_menu(it->second.menu_id); - } - - static bool idaapi s_cv_keydown(TCustomControl * /*cv*/, int vk_key, int shift, void *ud) - { - customviewer_t *_this = (customviewer_t *)ud; - return _this->on_keydown(vk_key, shift); - } - // The popup menu is being constructed - static void idaapi s_cv_popup(TCustomControl * /*cv*/, void *ud) - { - customviewer_t *_this = (customviewer_t *)ud; - _this->on_popup(); - } - // The user clicked - static bool idaapi s_cv_click(TCustomControl *cv, int shift, void *ud) - { - customviewer_t *_this = (customviewer_t *)ud; - return _this->on_click(shift); - } - // The user double clicked - static bool idaapi s_cv_dblclick(TCustomControl * /*cv*/, int shift, void *ud) - { - customviewer_t *_this = (customviewer_t *)ud; - return _this->on_dblclick(shift); - } - // Cursor position has been changed - static void idaapi s_cv_curpos(TCustomControl * /*cv*/, void *ud) - { - customviewer_t *_this = (customviewer_t *)ud; - _this->on_curpos_changed(); - } - - //-------------------------------------------------------------------------- - static int idaapi s_ui_cb(void *ud, int code, va_list va) - { - customviewer_t *_this = (customviewer_t *)ud; - switch ( code ) - { - case ui_get_custom_viewer_hint: - { - TCustomControl *viewer = va_arg(va, TCustomControl *); - place_t *place = va_arg(va, place_t *); - int *important_lines = va_arg(va, int *); - qstring &hint = *va_arg(va, qstring *); - return ((_this->_features & HAVE_HINT) == 0 || place == NULL || _this->_cv != viewer) ? 0 : (_this->on_hint(place, important_lines, hint) ? 1 : 0); - } - case ui_tform_invisible: - { - TForm *form = va_arg(va, TForm *); - if ( _this->_form != form ) - break; - unhook_from_notification_point(HT_UI, s_ui_cb, _this); - _this->on_close(); - _this->on_post_close(); - } - break; - } - return 0; - } - - void on_post_close() - { - init_vars(); - clear_popup_menu(); - } - -public: - // All the overridable callbacks - // OnClick - virtual bool on_click(int /*shift*/) { return false; } - // OnDblClick - virtual bool on_dblclick(int /*shift*/) { return false; } - // OnCurorPositionChanged - virtual void on_curpos_changed() { } - // OnHostFormClose - virtual void on_close() { } - // OnKeyDown - virtual bool on_keydown(int /*vk_key*/, int /*shift*/) { return false; } - // OnPopupShow - virtual bool on_popup() { return false; } - // OnHint - virtual bool on_hint(place_t * /*place*/, int * /*important_lines*/, qstring &/*hint*/) { return false; } - // OnPopupMenuClick - virtual bool on_popup_menu(size_t menu_id) { return false; } - - void init_vars() - { - _data = NULL; - _features = 0; - _curline.clear(); - _cv = NULL; - _form = NULL; - } - - customviewer_t() - { - init_vars(); - } - - ~customviewer_t() - { - } - - void close() - { - if ( _form != NULL ) - close_tform(_form, FORM_SAVE); - } - - bool set_range( - const place_t *minplace = NULL, - const place_t *maxplace = NULL) - { - if ( _cv == NULL ) - return false; - set_custom_viewer_range( - _cv, - minplace == NULL ? _data->get_min() : minplace, - maxplace == NULL ? _data->get_max() : maxplace); - return true; - } - - place_t *get_place( - bool mouse = false, - int *x = 0, - int *y = 0) - { - return _cv == NULL ? NULL : get_custom_viewer_place(_cv, mouse, x, y); - } - - //-------------------------------------------------------------------------- - bool refresh() - { - if ( _cv == NULL ) - return false; - refresh_custom_viewer(_cv); - return true; - } - - //-------------------------------------------------------------------------- - bool refresh_current(bool mouse = false) - { - int x, y; - place_t *pl = get_place(mouse, &x, &y); - if ( pl == NULL ) - return false; - return jumpto(pl, x, y); - } - - //-------------------------------------------------------------------------- - bool get_current_word(bool mouse, qstring &word) - { - // query the cursor position - int x, y; - if ( get_place(mouse, &x, &y) == NULL ) - return false; - - // query the line at the cursor - const char *line = get_current_line(mouse, true); - if ( line == NULL ) - return false; - - if ( x >= (int)strlen(line) ) - return false; - - // find the beginning of the word - const char *ptr = line + x; - while ( ptr > line && !isspace(ptr[-1]) ) - ptr--; - - // find the end of the word - const char *begin = ptr; - ptr = line + x; - while ( !isspace(*ptr) && *ptr != '\0' ) - ptr++; - - word.qclear(); - word.append(begin, ptr-begin); - return true; - } - - //-------------------------------------------------------------------------- - const char *get_current_line(bool mouse, bool notags) - { - const char *r = get_custom_viewer_curline(_cv, mouse); - if ( r == NULL || !notags ) - return r; - size_t sz = strlen(r); - if ( sz == 0 ) - return r; - _curline.resize(sz + 5, '\0'); - tag_remove(r, &_curline[0], sz + 1); - return _curline.c_str(); - } - - //-------------------------------------------------------------------------- - bool is_focused() - { - return get_current_viewer() == _cv; - } - - //-------------------------------------------------------------------------- - bool jumpto(place_t *place, int x, int y) - { - return ::jumpto(_cv, place, x, y); - } - - //-------------------------------------------------------------------------- - void clear_popup_menu() - { - if ( _cv != NULL ) - set_custom_viewer_popup_menu(_cv, NULL); - - for (intvec_t::iterator it=_installed_popups.begin(), it_end=_installed_popups.end(); - it != it_end; - ++it) - { - _global_popup_map.erase(*it); - } - _installed_popups.clear(); - } - - //-------------------------------------------------------------------------- - size_t add_popup_menu( - const char *title, - const char *hotkey) - { - size_t menu_id = _global_popup_id + 1; - // Overlap / already exists? - if (_cv == NULL || // No custviewer? - menu_id == 0 || // Overlap? - _global_popup_map.find(menu_id) != _global_popup_map.end()) // Already exists? - { - return 0; - } - add_custom_viewer_popup_item(_cv, title, hotkey, s_popup_menu_cb, (void *)menu_id); - - // Save global association - _global_popup_map[menu_id] = pyw_popupctx_t(menu_id, this); - _global_popup_id = menu_id; - - // Remember what menu IDs are set with this form - _installed_popups.push_back(menu_id); - return menu_id; - } - - //-------------------------------------------------------------------------- - bool create(const char *title, int features, custviewer_data_t *data) - { - // Already created? (in the instance) - if ( _form != NULL ) - return true; - - // Already created? (in IDA windows list) - HWND hwnd(NULL); - TForm *form = create_tform(title, &hwnd); - if ( hwnd == NULL ) - return false; - - _title = title; - _data = data; - _form = form; - _features = features; - - // Create the viewer - _cv = create_custom_viewer( - title, - (TWinControl *)_form, - _data->get_min(), - _data->get_max(), - _data->get_min(), - 0, - _data->get_ud()); - - // Set user-data - set_custom_viewer_handler(_cv, CVH_USERDATA, (void *)this); - - // - // Set other optional callbacks - // - if ( (features & HAVE_KEYDOWN) != 0 ) - set_custom_viewer_handler(_cv, CVH_KEYDOWN, (void *)s_cv_keydown); - - if ( (features & HAVE_POPUP) != 0 ) - set_custom_viewer_handler(_cv, CVH_POPUP, (void *)s_cv_popup); - - if ( (features & HAVE_DBLCLICK) != 0 ) - set_custom_viewer_handler(_cv, CVH_DBLCLICK, (void *)s_cv_dblclick); - - if ( (features & HAVE_CURPOS) != 0 ) - set_custom_viewer_handler(_cv, CVH_CURPOS, (void *)s_cv_curpos); - - if ( (features & HAVE_CLICK) != 0 ) - set_custom_viewer_handler(_cv, CVH_CLICK, (void *)s_cv_click); - - // Hook to UI notifications (for TForm close event) - hook_to_notification_point(HT_UI, s_ui_cb, this); - - return true; - } - - //-------------------------------------------------------------------------- - bool show() - { - if ( _form == NULL ) - return false; - open_tform(_form, FORM_TAB|FORM_MENU|FORM_RESTORE); - return true; - } -}; - -customviewer_t::pyw_popupmap_t customviewer_t::_global_popup_map; -size_t customviewer_t::_global_popup_id = 0; -//--------------------------------------------------------------------------- -class py_simplecustview_t: public customviewer_t -{ -private: - cvdata_simpleline_t data; - PyObject *py_self, *py_this, *py_last_link; - int features; - - // Convert a tuple (String, [color, [bgcolor]]) to a simpleline_t - static bool py_to_simpleline(PyObject *py, simpleline_t &sl) - { - if ( PyString_Check(py) ) - { - sl.line = PyString_AsString(py); - return true; - } - Py_ssize_t sz; - if ( !PyTuple_Check(py) || (sz = PyTuple_Size(py)) <= 0 ) - return false; - PyObject *py_val = PyTuple_GetItem(py, 0); - if ( !PyString_Check(py_val) ) - return false; - sl.line = PyString_AsString(py_val); - - if ( (sz > 1) && (py_val = PyTuple_GetItem(py, 1)) && PyLong_Check(py_val) ) - sl.color = color_t(PyLong_AsUnsignedLong(py_val)); - - if ( (sz > 2) && (py_val = PyTuple_GetItem(py, 2)) && PyLong_Check(py_val) ) - sl.bgcolor = PyLong_AsUnsignedLong(py_val); - return true; - } - - // - // Callbacks - // - virtual bool on_click(int shift) - { - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift); - PyShowErr(S_ON_CLICK); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; - } - - // OnDblClick - virtual bool on_dblclick(int shift) - { - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift); - PyShowErr(S_ON_DBL_CLICK); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; - } - - // OnCurorPositionChanged - virtual void on_curpos_changed() - { - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL); - PyShowErr(S_ON_CURSOR_POS_CHANGED); - Py_XDECREF(py_result); - } - - // OnHostFormClose - virtual void on_close() - { - // Call the close method if it is there and the object is still bound - if ( (features & HAVE_CLOSE) != 0 && py_self != NULL ) - { - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL); - PyShowErr(S_ON_CLOSE); - Py_XDECREF(py_result); - - // Cleanup - Py_DECREF(py_self); - py_self = NULL; - } - } - - // OnKeyDown - virtual bool on_keydown(int vk_key, int shift) - { - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_KEYDOWN, "ii", vk_key, shift); - PyShowErr(S_ON_KEYDOWN); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; - } - - // OnPopupShow - virtual bool on_popup() - { - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_POPUP, NULL); - PyShowErr(S_ON_POPUP); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; - } - - // OnHint - virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) - { - size_t ln = data.to_lineno(place); - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_HINT, PY_FMT64, pyul_t(ln)); - PyShowErr(S_ON_HINT); - bool ok = py_result != NULL && PyString_Check(py_result); - if ( ok ) - { - if ( important_lines != NULL ) - *important_lines = 0; - hint = PyString_AsString(py_result); - } - Py_XDECREF(py_result); - return ok; - } - - // OnPopupMenuClick - virtual bool on_popup_menu(size_t menu_id) - { - PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_POPUP_MENU, PY_FMT64, pyul_t(menu_id)); - PyShowErr(S_ON_POPUP_MENU); - bool ok = py_result != NULL && PyObject_IsTrue(py_result); - Py_XDECREF(py_result); - return ok; - } - - void refresh_range() - { - data.set_minmax(); - set_range(); - } - -public: - py_simplecustview_t() - { - py_this = py_self = py_last_link = NULL; - } - ~py_simplecustview_t() - { - } - // Edits an existing line - bool edit_line(size_t nline, PyObject *py_sl) - { - simpleline_t sl; - if ( !py_to_simpleline(py_sl, sl) ) - return false; - return data.set_line(nline, sl); - } - - // Low level: patches a line string directly - bool patch_line(size_t nline, size_t offs, int value) - { - return data.patch_line(nline, offs, value); - } - - // Insert a line - bool insert_line(size_t nline, PyObject *py_sl) - { - simpleline_t sl; - if ( !py_to_simpleline(py_sl, sl) ) - return false; - return data.insert_line(nline, sl); - } - - // Adds a line tuple - bool add_line(PyObject *py_sl) - { - simpleline_t sl; - if ( !py_to_simpleline(py_sl, sl) ) - return false; - data.add_line(sl); - refresh_range(); - return true; - } - - bool del_line(size_t nline) - { - bool ok = data.del_line(nline); - if ( ok ) - refresh_range(); - return ok; - } - - // Gets the position and returns a tuple (lineno, x, y) - PyObject *get_pos(bool mouse) - { - place_t *pl; - int x, y; - pl = get_place(mouse, &x, &y); - if ( pl == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("(" PY_FMT64 "ii)", pyul_t(data.to_lineno(pl)), x, y); - } - - // Returns the line tuple - PyObject *get_line(size_t nline) - { - simpleline_t *r = data.get_line(nline); - if ( r == NULL ) - Py_RETURN_NONE; - return Py_BuildValue("(sII)", r->line.c_str(), (unsigned int)r->color, (unsigned int)r->bgcolor); - } - - // Returns the count of lines - const size_t count() const - { - return data.count(); - } - - // Clears lines - void clear() - { - data.clear_lines(); - refresh_range(); - } - - bool jumpto(size_t ln, int x, int y) - { - return customviewer_t::jumpto(&simpleline_place_t(ln), x, y); - } - - // Initializes and links the Python object to this class - bool init(PyObject *py_link, const char *title) - { - // Already created? - if ( _form != NULL ) - return true; - - // Probe callbacks - features = 0; - static struct - { - const char *cb_name; - int feature; - } const cbtable[] = - { - {S_ON_CLICK, HAVE_CLICK}, - {S_ON_CLOSE, HAVE_CLOSE}, - {S_ON_HINT, HAVE_HINT}, - {S_ON_KEYDOWN, HAVE_KEYDOWN}, - {S_ON_POPUP, HAVE_POPUP}, - {S_ON_DBL_CLICK, HAVE_DBLCLICK}, - {S_ON_CURSOR_POS_CHANGED, HAVE_CURPOS} - }; - for ( size_t i=0; i - -bool idaapi py_menu_item_callback(void *userdata) -{ - PyObject *func, *args, *result; - bool ret = 0; - - // userdata is a tuple of ( func, args ) - // func and args are borrowed references from userdata - func = PyTuple_GET_ITEM(userdata, 0); - args = PyTuple_GET_ITEM(userdata, 1); - - // call the python function - result = PyEval_CallObject(func, args); - - // we cannot raise an exception in the callback, just print it. - if (!result) { - PyErr_Print(); - return 0; - } - - // if the function returned a non-false value, then return 1 to ida, - // overwise return 0 - if (PyObject_IsTrue(result)) { - ret = 1; - } - Py_DECREF(result); - - return ret; -} -%} - -%rename (add_menu_item) wrap_add_menu_item; -%inline %{ -// -#ifdef __NT__ -// -// Pywraps Simple Custom Viewer functions -// -PyObject *pyscv_init(PyObject *py_link, const char *title) -{ - py_simplecustview_t *_this = new py_simplecustview_t(); - bool ok = _this->init(py_link, title); - if ( !ok ) - { - delete _this; - Py_RETURN_NONE; - } - return _this->get_pythis(); -} -#define DECL_THIS py_simplecustview_t *_this = py_simplecustview_t::get_this(py_this) - -bool pyscv_refresh(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->refresh(); -} - -bool pyscv_delete(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - _this->close(); - delete _this; - return true; -} - -bool pyscv_refresh_current(PyObject *py_this, bool mouse) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->refresh_current(mouse); -} - -PyObject *pyscv_get_current_line(PyObject *py_this, bool mouse, bool notags) -{ - DECL_THIS; - const char *line; - if ( _this == NULL || (line = _this->get_current_line(mouse, notags)) == NULL ) - Py_RETURN_NONE; - return PyString_FromString(line); -} - -bool pyscv_is_focused(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->is_focused(); -} - -void pyscv_clear_popup_menu(PyObject *py_this) -{ - DECL_THIS; - if ( _this != NULL ) - _this->clear_popup_menu(); -} - -size_t pyscv_add_popup_menu(PyObject *py_this, const char *title, const char *hotkey) -{ - DECL_THIS; - return _this == NULL ? 0 : _this->add_popup_menu(title, hotkey); -} - -size_t pyscv_count(PyObject *py_this) -{ - DECL_THIS; - return _this == NULL ? 0 : _this->count(); -} - -bool pyscv_show(PyObject *py_this) -{ - DECL_THIS; - return _this == NULL ? false : _this->show(); -} - -void pyscv_close(PyObject *py_this) -{ - DECL_THIS; - if ( _this != NULL ) - _this->close(); -} - -bool pyscv_jumpto(PyObject *py_this, size_t ln, int x, int y) -{ - DECL_THIS; - if ( _this == NULL ) - return false; - return _this->jumpto(ln, x, y); -} - -// Returns the line tuple -PyObject *pyscv_get_line(PyObject *py_this, size_t nline) -{ - DECL_THIS; - if ( _this == NULL ) - Py_RETURN_NONE; - return _this->get_line(nline); -} - -// Gets the position and returns a tuple (lineno, x, y) -PyObject *pyscv_get_pos(PyObject *py_this, bool mouse) -{ - DECL_THIS; - if ( _this == NULL ) - Py_RETURN_NONE; - return _this->get_pos(mouse); -} - -PyObject *pyscv_clear_lines(PyObject *py_this) -{ - DECL_THIS; - if ( _this != NULL ) - _this->clear(); - Py_RETURN_NONE; -} - -// Adds a line tuple -bool pyscv_add_line(PyObject *py_this, PyObject *py_sl) -{ - DECL_THIS; - return _this == NULL ? false : _this->add_line(py_sl); -} - -bool pyscv_insert_line(PyObject *py_this, size_t nline, PyObject *py_sl) -{ - DECL_THIS; - return _this == NULL ? false : _this->insert_line(nline, py_sl); -} - -bool pyscv_patch_line(PyObject *py_this, size_t nline, size_t offs, int value) -{ - DECL_THIS; - return _this == NULL ? false : _this->patch_line(nline, offs, value); -} - -bool pyscv_del_line(PyObject *py_this, size_t nline) -{ - DECL_THIS; - return _this == NULL ? false : _this->del_line(nline); -} - -PyObject *pyscv_get_selection(PyObject *py_this) -{ - DECL_THIS; - if ( _this == NULL ) - Py_RETURN_NONE; - return _this->py_get_selection(); -} - -PyObject *pyscv_get_current_word(PyObject *py_this, bool mouse) -{ - DECL_THIS; - if ( _this != NULL ) - { - qstring word; - if ( _this->get_current_word(mouse, word) ) - return PyString_FromString(word.c_str()); - } - Py_RETURN_NONE; -} - -// Edits an existing line -bool pyscv_edit_line(PyObject *py_this, size_t nline, PyObject *py_sl) -{ - DECL_THIS; - return _this == NULL ? false : _this->edit_line(nline, py_sl); -} -#undef DECL_THIS -#endif -// - -// -#ifdef CH_ATTRS -PyObject *choose2_find(const char *title); -#endif -int choose2_add_command(PyObject *self, const char *caption, int flags, int menu_index, int icon); -void choose2_refresh(PyObject *self); -void choose2_close(PyObject *self); -int choose2_show(PyObject *self); -void choose2_activate(PyObject *self); -// - -bool wrap_add_menu_item ( - const char *menupath, - const char *name, - const char *hotkey, - int flags, - PyObject *pyfunc, - PyObject *args) { - // FIXME: probably should keep track of this data, and destroy it when the menu item is removed - PyObject *cb_data; - - if (args == Py_None) { - Py_DECREF(Py_None); - args = PyTuple_New( 0 ); - if (!args) - return 0; - } - - if(!PyTuple_Check(args)) { - PyErr_SetString(PyExc_TypeError, "args must be a tuple or None"); - return 0; - } - - cb_data = Py_BuildValue("(OO)", pyfunc, args); - return add_menu_item(menupath, name, hotkey, flags, py_menu_item_callback, (void *)cb_data); -} -%} - -%include "kernwin.hpp" - -uint32 choose_choose(PyObject *self, - int flags, - int x0,int y0, - int x1,int y1, - int width); -%{ - -// - -//------------------------------------------------------------------------ -// Some defines -#define POPUP_NAMES_COUNT 4 -#define MAX_CHOOSER_MENU_COMMANDS 10 -#define thisobj ((py_choose2_t *) obj) -#define thisdecl py_choose2_t *_this = thisobj -#define MENU_COMMAND_CB(id) static uint32 idaapi s_menu_command_##id(void *obj, uint32 n) { return thisobj->on_command(id, int(n)); } - -//------------------------------------------------------------------------ -// Helper functions -class py_choose2_t; -typedef std::map pychoose2_to_choose2_map_t; -static pychoose2_to_choose2_map_t choosers; - -py_choose2_t *choose2_find_instance(PyObject *self) -{ - pychoose2_to_choose2_map_t::iterator it = choosers.find(self); - if ( it == choosers.end() ) - return NULL; - return it->second; -} - -void choose2_add_instance(PyObject *self, py_choose2_t *c2) -{ - choosers[self] = c2; -} - -void choose2_del_instance(PyObject *self) -{ - pychoose2_to_choose2_map_t::iterator it = choosers.find(self); - if ( it != choosers.end() ) - choosers.erase(it); -} - -//------------------------------------------------------------------------ -class py_choose2_t -{ -private: - enum - { - CHOOSE2_HAVE_DEL = 0x0001, - CHOOSE2_HAVE_INS = 0x0002, - CHOOSE2_HAVE_UPDATE = 0x0004, - CHOOSE2_HAVE_EDIT = 0x0008, - CHOOSE2_HAVE_ENTER = 0x0010, - CHOOSE2_HAVE_GETICON = 0x0020, - CHOOSE2_HAVE_GETATTR = 0x0040, - CHOOSE2_HAVE_COMMAND = 0x0080, - CHOOSE2_HAVE_ONCLOSE = 0x0100 - }; - int flags; - int cb_flags; - qstring title; - PyObject *self; - qstrvec_t cols; - // the number of declarations should follow the MAX_CHOOSER_MENU_COMMANDS value - MENU_COMMAND_CB(0) MENU_COMMAND_CB(1) - MENU_COMMAND_CB(2) MENU_COMMAND_CB(3) - MENU_COMMAND_CB(4) MENU_COMMAND_CB(5) - MENU_COMMAND_CB(6) MENU_COMMAND_CB(7) - MENU_COMMAND_CB(8) MENU_COMMAND_CB(9) - static chooser_cb_t *menu_cbs[MAX_CHOOSER_MENU_COMMANDS]; - int menu_cb_idx; - //------------------------------------------------------------------------ - // Static methods to dispatch to member functions - //------------------------------------------------------------------------ - static int idaapi ui_cb(void *obj, int notification_code, va_list va) - { - if ( notification_code != ui_get_chooser_item_attrs ) - return 0; - va_arg(va, void *); - int n = int(va_arg(va, uint32)); - chooser_item_attrs_t *attr = va_arg(va, chooser_item_attrs_t *); - thisobj->on_get_line_attr(n, attr); - return 1; - } - static uint32 idaapi s_sizer(void *obj) - { - return (uint32)thisobj->on_get_size(); - } - static void idaapi s_getl(void *obj, uint32 n, char * const *arrptr) - { - thisobj->on_get_line(int(n), arrptr); - } - static uint32 idaapi s_del(void *obj, uint32 n) - { - return uint32(thisobj->on_delete_line(int(n))); - } - static void idaapi s_ins(void *obj) - { - thisobj->on_insert_line(); - } - static uint32 idaapi s_update(void *obj, uint32 n) - { - return uint32(thisobj->on_refresh(int(n))); - } - static void idaapi s_edit(void *obj, uint32 n) - { - thisobj->on_edit_line(int(n)); - } - static void idaapi s_enter(void * obj, uint32 n) - { - thisobj->on_select_line(int(n)); - } - static int idaapi s_get_icon(void *obj, uint32 n) - { - return thisobj->on_get_icon(int(n)); - } - static void idaapi s_destroy(void *obj) - { - thisobj->on_close(); - } -private: - //------------------------------------------------------------------------ - // Member functions corresponding to each chooser2() callback - //------------------------------------------------------------------------ - void on_get_line(int lineno, char * const *line_arr) - { - if ( lineno == 0 ) - { - for ( size_t i=0; i=0; i-- ) - line_arr[i][0] = '\0'; - - // Call Python - PyObject *list = PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1); - if ( list == NULL ) - return; - for ( int i=ncols-1; i>=0; i-- ) - { - PyObject *item = PyList_GetItem(list, Py_ssize_t(i)); - if ( item == NULL ) - continue; - const char *str = PyString_AsString(item); - if ( str != NULL ) - qstrncpy(line_arr[i], str, MAXSTR); - } - Py_DECREF(list); - } - - size_t on_get_size() - { - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL); - if ( pyres == NULL ) - return 0; - size_t res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res; - } - - void on_close() - { -#ifdef CH_ATTRS - if ( (flags & CH_ATTRS) != 0 ) - unhook_from_notification_point(HT_UI, ui_cb, this); -#endif - // Call Python - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL); - Py_XDECREF(pyres); - Py_XDECREF(self); - - // Remove from list - choose2_del_instance(self); - - // delete this instance if none modal - if ( (flags & CH_MODAL) == 0 ) - delete this; -} - - int on_delete_line(int lineno) - { - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_DELETE_LINE, "i", lineno - 1); - if ( pyres == NULL ) - return lineno; - size_t res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res + 1; - } - - int on_refresh(int lineno) - { - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_REFRESH, "i", lineno - 1); - if ( pyres == NULL ) - return lineno; - size_t res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res + 1; - } - - void on_insert_line() - { - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL); - Py_XDECREF(pyres); - } - - void on_select_line(int lineno) - { - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_SELECT_LINE, "i", lineno - 1); - Py_XDECREF(pyres); - } - - void on_edit_line(int lineno) - { - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_EDIT_LINE, "i", lineno - 1); - Py_XDECREF(pyres); - } - - int on_command(int cmd_id, int lineno) - { - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_COMMAND, "ii", lineno - 1, cmd_id); - if ( pyres==NULL ) - return lineno; - size_t res = PyInt_AsLong(pyres); - Py_XDECREF(pyres); - return res; - } - - int on_get_icon(int lineno) - { - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_ICON, "i", lineno - 1); - size_t res = PyInt_AsLong(pyres); - Py_XDECREF(pyres); - return res; - } - void on_get_line_attr(int lineno, chooser_item_attrs_t *attr) - { - PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1); - if ( pyres == NULL ) - return; - - if ( PyList_Check(pyres) ) - { - PyObject *item; - if ( (item = PyList_GetItem(pyres, 0)) != NULL ) - attr->color = PyInt_AsLong(item); - if ( (item = PyList_GetItem(pyres, 1)) != NULL ) - attr->flags = PyInt_AsLong(item); - } - Py_XDECREF(pyres); - } -public: - //------------------------------------------------------------------------ - // Public methods - //------------------------------------------------------------------------ - py_choose2_t() - { - flags = 0; - cb_flags = 0; - menu_cb_idx = 0; - self = NULL; - } - static py_choose2_t *find_chooser(const char *title) - { - return (py_choose2_t *) get_chooser_obj(title); - } - void close() - { - close_chooser(title.c_str()); - } - bool activate() - { - TForm *frm = find_tform(title.c_str()); - if ( frm == NULL ) - return false; - switchto_tform(frm, true); - return true; - } - - int choose2( - int fl, - int ncols, - const int *widths, - const char *title, - int deflt = -1, - // An array of 4 strings: ("Insert", "Delete", "Edit", "Refresh" - const char * const *popup_names = NULL, - int icon = -1, - int x1 = -1, int y1 = -1, int x2 = -1, int y2 = -1) - { - flags = fl; - if ( (flags & CH_ATTRS) != 0 ) - { - if ( !hook_to_notification_point(HT_UI, ui_cb, this) ) - flags &= ~CH_ATTRS; - } - this->title = title; - return ::choose2( - flags, - x1, y1, x2, y2, - this, - ncols, widths, - s_sizer, - s_getl, - title, - icon, - deflt, - cb_flags & CHOOSE2_HAVE_DEL ? s_del : NULL, - cb_flags & CHOOSE2_HAVE_INS ? s_ins : NULL, - cb_flags & CHOOSE2_HAVE_UPDATE ? s_update : NULL, - cb_flags & CHOOSE2_HAVE_EDIT ? s_edit : NULL, - cb_flags & CHOOSE2_HAVE_ENTER ? s_enter : NULL, - s_destroy, - popup_names, - cb_flags & CHOOSE2_HAVE_GETICON ? s_get_icon : NULL); - } - - int add_command(const char *caption, int flags=0, int menu_index=-1, int icon=-1) - { - if ( menu_cb_idx >= MAX_CHOOSER_MENU_COMMANDS ) - return -1; - bool ret = add_chooser_command(title.c_str(), caption, menu_cbs[menu_cb_idx], menu_index, icon, flags); - if ( !ret ) - return -1; - return menu_cb_idx++; - } - - int show(PyObject *self) - { - PyObject *attr; - // get title - if ( (attr = PyObject_TryGetAttrString(self, "title")) == NULL ) - return -1; - qstring title = PyString_AsString(attr); - Py_DECREF(attr); - - // get flags - if ( (attr = PyObject_TryGetAttrString(self, "flags")) == NULL ) - return -1; - int flags = PyInt_AsLong(attr); - Py_DECREF(attr); - - // get columns - if ( (attr = PyObject_TryGetAttrString(self, "cols")) == NULL ) - return -1; - - // get col count - int ncols = PyList_Size(attr); - - // get cols caption and widthes - intvec_t widths; - cols.qclear(); - for ( int i=0; iself = self; - - // Create chooser - int r = this->choose2(flags, ncols, &widths[0], title.c_str(), deflt, popup_names, icon, pts[0], pts[1], pts[2], pts[3]); - - // Clear temporary popup_names - if ( popup_names != NULL ) - { - for ( int i=0; iactivate(); - return 1; - } - c2 = new py_choose2_t(); - choose2_add_instance(self, c2); - return c2->show(self); -} - -//------------------------------------------------------------------------ -void choose2_close(PyObject *self) -{ - py_choose2_t *c2 = choose2_find_instance(self); - if ( c2 != NULL ) - c2->close(); -} - -//------------------------------------------------------------------------ -void choose2_refresh(PyObject *self) -{ - py_choose2_t *c2 = choose2_find_instance(self); - if ( c2 != NULL ) - c2->refresh(); -} - -//------------------------------------------------------------------------ -void choose2_activate(PyObject *self) -{ - py_choose2_t *c2 = choose2_find_instance(self); - if ( c2 != NULL ) - c2->activate(); -} - -//------------------------------------------------------------------------ -int choose2_add_command(PyObject *self, const char *caption, int flags=0, int menu_index=-1, int icon=-1) -{ - py_choose2_t *c2 = choose2_find_instance(self); - if ( c2 != NULL ) - return c2->add_command(caption, flags, menu_index, icon); - else - return -2; -} - -//------------------------------------------------------------------------ -#ifdef CH_ATTRS -PyObject *choose2_find(const char *title) -{ - py_choose2_t *c2 = py_choose2_t::find_chooser(title); - if ( c2 == NULL ) - return NULL; - return c2->get_self(); -} -#endif -// -uint32 idaapi choose_sizer(void *self) -{ - PyObject *pyres; - uint32 res; - - pyres = PyObject_CallMethod((PyObject *)self, "sizer", ""); - res = PyInt_AsLong(pyres); - Py_DECREF(pyres); - return res; -} - -char * idaapi choose_getl(void *self, uint32 n, char *buf) -{ - PyObject *pyres; - char *res; - - pyres = PyObject_CallMethod((PyObject *)self, "getl", "l", n); - - if (!pyres) - { - strcpy(buf, ""); - return buf; - } - - res = PyString_AsString(pyres); - - if (res) - { - strncpy(buf, res, MAXSTR); - res = buf; - } - else - { - strcpy(buf, ""); - res = buf; - } - - Py_DECREF(pyres); - return res; -} - -void idaapi choose_enter(void *self, uint32 n) -{ - PyObject_CallMethod((PyObject *)self, "enter", "l", n); - return; -} - -uint32 choose_choose(void *self, - int flags, - int x0,int y0, - int x1,int y1, - int width) -{ - PyObject *pytitle; - const char *title; - if ((pytitle = PyObject_GetAttrString((PyObject *)self, "title"))) - { - title = PyString_AsString(pytitle); - } - else - { - title = "Choose"; - pytitle = NULL; - } - int r = choose( - flags, - x0, y0, - x1, y1, - self, - width, - &choose_sizer, - &choose_getl, - title, - 1, - 1, - NULL, /* del */ - NULL, /* inst */ - NULL, /* update */ - NULL, /* edit */ - &choose_enter, - NULL, /* destroy */ - NULL, /* popup_names */ - NULL /* get_icon */ - ); - Py_XDECREF(pytitle); - return r; -} -%} - -%pythoncode %{ - -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 & 1) == 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 - """ - return _idaapi.choose_choose(self, self.flags, self.x0, self.y0, self.x1, self.y1, self.width) -%} - -#ifdef __NT__ -%pythoncode %{ -# -class simplecustviewer_t(object): - - def __init__(self): - self.this = None - - def __del__(self): - """Destructor. It also frees the associated C++ object""" - try: - _idaapi.pyscv_delete(self.this) - except: - pass - - @staticmethod - def make_sl_arg(line, fgcolor=None, bgcolor=None): - return line if (fgcolor is None and bgcolor is None) else (line, fgcolor, bgcolor) - - def Create(self, title): - """ - Creates the custom view. This should be the first method called after instantiation - - @param title: The title of the view - @return: Boolean whether it succeeds or fails. It may fail if a window with the same title is already open. - In this case better close existing windows - """ - self.title = title - self.this = _idaapi.pyscv_init(self, title) - return True if self.this else False - - def Close(self): - """ - Destroys the view. - One has to call Create() afterwards. - Show() can be called and it will call Create() internally. - @return: Boolean - """ - return _idaapi.pyscv_close(self.this) - - def Show(self): - """ - Shows an already created view. It the view was close, then it will call Create() for you - @return: Boolean - """ - return _idaapi.pyscv_show(self.this) - - def Refresh(self): - return _idaapi.pyscv_refresh(self.this) - - def RefreshCurrent(self, mouse = 0): - """Refreshes the current line only""" - return _idaapi.pyscv_refresh_current(self.this, mouse) - - def Count(self): - """Returns the number of lines in the view""" - return _idaapi.pyscv_count(self.this) - - def GetSelection(self): - """ - Returns the selected area or None - @return: - - tuple(x1, y1, x2, y2) - - None if no selection - """ - return _idaapi.pyscv_get_selection(self.this) - - def ClearLines(self): - """Clears all the lines""" - _idaapi.pyscv_clear_lines(self.this) - - def AddLine(self, line, fgcolor=None, bgcolor=None): - """ - Adds a colored line to the view - @return: Boolean - """ - return _idaapi.pyscv_add_line(self.this, self.make_sl_arg(line, fgcolor, bgcolor)) - - def InsertLine(self, lineno, line, fgcolor=None, bgcolor=None): - """ - Inserts a line in the given position - @return Boolean - """ - return _idaapi.pyscv_insert_line(self.this, lineno, self.make_sl_arg(line, fgcolor, bgcolor)) - - def EditLine(self, lineno, line, fgcolor=None, bgcolor=None): - """ - Edits an existing line. - @return Boolean - """ - return _idaapi.pyscv_edit_line(self.this, lineno, self.make_sl_arg(line, fgcolor, bgcolor)) - - def PatchLine(self, lineno, offs, value): - """Patches an existing line character at the given offset. This is a low level function. You must know what you're doing""" - return _idaapi.pyscv_patch_line(self.this, lineno, offs, value) - - def DelLine(self, lineno): - """ - Deletes an existing line - @return Boolean - """ - return _idaapi.pyscv_del_line(self.this, lineno) - - def GetLine(self, lineno): - """ - Returns a line - @param lineno: The line number - @return: - Returns a tuple (colored_line, fgcolor, bgcolor) or None - """ - return _idaapi.pyscv_get_line(self.this, lineno) - - def GetCurrentWord(self, mouse = 0): - """ - Returns the current word - @param mouse: Use mouse position or cursor position - @return: None if failed or a String containing the current word at mouse or cursor - """ - return _idaapi.pyscv_get_current_word(self.this, mouse) - - def GetCurrentLine(self, mouse = 0, notags = 0): - """ - Returns the current line. - @param mouse: Current line at mouse pos - @param notags: If True then tag_remove() will be called before returning the line - @return: Returns the current line (colored or uncolored) - """ - return _idaapi.pyscv_get_current_line(self.this, mouse, notags) - - def GetPos(self, mouse = 0): - """ - Returns the current cursor or mouse position. - @param mouse: return mouse position - @return: Returns a tuple (lineno, x, y) - """ - return _idaapi.pyscv_get_pos(self.this, mouse) - - def GetLineNo(self, mouse = 0): - """Calls GetPos() and returns the current line number only or None on failure""" - r = self.GetPos(mouse) - return None if not r else r[0] - - def Jump(self, lineno, x=0, y=0): - return _idaapi.pyscv_jumpto(self.this, lineno, x, y) - - def AddPopupMenu(self, title, hotkey=""): - """ - Adds a popup menu item - @param title: The name of the menu item - @param hotkey: Hotkey of the item or just empty - @return: Returns the - """ - return _idaapi.pyscv_add_popup_menu(self.this, title, hotkey) - - def ClearPopupMenu(self): - """ - Clears all previously installed popup menu items. - Use this function if you're generating menu items on the fly (in the OnPopup() callback), - and before adding new items - """ - _idaapi.pyscv_clear_popup_menu(self.this) - - def IsFocused(self): - """Returns True if the current view is the focused view""" - return _idaapi.pyscv_is_focused(self.this) - - # Here are all the supported events - # Uncomment any event to enable -# def OnClick(self, shift): -# """ -# User clicked in the view -# @param shift: Shift flag -# @return Boolean. True if you handled the event -# """ -# print "OnClick, shift=%d" % shift -# return True -# -# def OnDblClick(self, shift): -# """ -# User dbl-clicked in the view -# @param shift: Shift flag -# @return Boolean. True if you handled the event -# """ -# print "OnDblClick, shift=%d" % shift -# return True -# -# def OnCursorPosChanged(self): -# """ -# Cursor position changed. -# @return Nothing -# """ -# print "OnCurposChanged" -# -# def OnClose(self): -# """ -# The view is closing. Use this event to cleanup. -# @return Nothing -# """ -# print "OnClose" -# -# 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 -# """ -# print "OnKeydown, vk=%d shift=%d" % (vkey, shift) -# return False -# -# def OnPopup(self): -# """ -# Context menu popup is about to be shown. Create items dynamically if you wish -# @return Boolean. True if you handled the event -# """ -# print "OnPopup" -# -# def OnHint(self, lineno): -# """ -# Hint requested for the given line number. -# @param lineno: The line number (zero based) -# @return: -# - string: a string containing the hint -# - None: if no hint available -# """ -# return "OnHint, line=%d" % lineno -# -# def OnPopupMenu(self, menu_id): -# """ -# A context (or popup) menu item was executed. -# @param menu_id: ID previously registered with add_popup_menu() -# @return: Boolean -# """ -# print "OnPopupMenu, menu_id=" % menu_id -# return True -%} -#endif // __NT__ -# - -%pythoncode %{ -# -class Choose2: - """Choose2 wrapper class""" - - # refer to kernwin.hpp for more information on how to use these constants - CH_MODAL = 0x01 - CH_MULTI = 0x02 - CH_MULTI_EDIT = 0x04 - CH_NOBTNS = 0x08 - CH_ATTRS = 0x10 - CH_BUILTIN_MASK = 0xF80000 - - # column flags (are specified in the widths array) - CHCOL_PLAIN = 0x00000000 - CHCOL_PATH = 0x00010000 - CHCOL_HEX = 0x00020000 - CHCOL_DEC = 0x00030000 - CHCOL_FORMAT = 0x00070000 - - def __init__(self, title, cols, flags=0, popup_names=None, icon=-1, x1=-1, y1=-1, x2=-1, y2=-1): - self.title = title - self.flags = flags - # a list of colums; each list item is a list of two items - # example: [ ["Address", 10 | Choose2.CHCOL_HEX], ["Name", 30 | CHCOL_PLAIN] ] - self.cols = cols - self.deflt = -1 - # list of new captions to replace this list ["Insert", "Delete", "Edit", "Refresh"] - self.popup_names = popup_names - self.icon = icon - self.x1 = x1 - self.y1 = y1 - self.x2 = x2 - self.y2 = y2 - - def Show(self, modal=False): - """Activates or creates a chooser window""" - if modal: - self.flags |= Choose2.CH_MODAL - else: - self.flags &= ~Choose2.CH_MODAL - return _idaapi.choose2_show(self) - - def Activate(self): - """Activates a visible chooser""" - return _idaapi.choose2_activate(self) - - def Refresh(self): - """Causes the refresh callback to trigger""" - return _idaapi.choose2_refresh(self) - - def Close(self): - """Closes the chooser""" - return _idaapi.choose2_close(self) - - def AddCommand(self, caption, flags = _idaapi.CHOOSER_POPUP_MENU, menu_index=-1,icon = -1): - """Adds a new chooser command - Save the returned value and later use it in the OnCommand handler - - @return: Returns a negative value on failure or the command index - """ - return _idaapi.choose2_add_command(self, caption, flags, menu_index, icon) - - # - # Implement these methods in the subclass: - # - -# def OnClose(self): -# # return nothing -# pass - -# def OnEditLine(self, n): -# # return nothing (mandatory callback) -# pass - -# def OnInsertLine(self): -# # return nothing -# pass - -# def OnSelectLine(self, n): -# # return nothing -# pass - -# def OnGetLine(self, n): -# # return a list [col1, col2, col3, ...] describing the n-th line -# return ["col1", "col2", ...] - -# def OnGetSize(self): -# # return the size (mandatory callback) -# return len(self.the_list) - -# def OnDeleteLine(self, n): -# # return new line number -# return self.n - -# def OnRefresh(self, n): -# # return new line number -# return self.n - -# def OnCommand(self, n, cmd_id): -# # return int ; check add_chooser_command() -# return 0 - -# def OnGetIcon(self, n): -# # return icon number (or -1) -# return -1 - -# def OnGetLineAttr(self, n): -# # return list [color, flags] or None; check chooser_item_attrs_t -# pass -# -%} +// Ignore the va_list functions +%ignore AskUsingForm_cv; +%ignore close_form; +%ignore vaskstr; +%ignore vasktext; +%ignore add_menu_item; +%rename (add_menu_item) py_add_menu_item; +%ignore del_menu_item; +%rename (del_menu_item) py_del_menu_item; +%ignore vwarning; +%ignore vinfo; +%ignore vnomem; +%ignore vmsg; +%ignore show_wait_box_v; +%ignore askbuttons_cv; +%ignore askfile_cv; +%ignore askyn_cv; +%ignore askyn_v; +// Ignore these string functions. There are trivial replacements in Python. +%ignore addblanks; +%ignore trim; +%ignore skipSpaces; +%ignore stristr; + +// Ignore the cli_t class +%ignore cli_t; + +%include "typemaps.i" + +%rename (asktext) py_asktext; +%rename (str2ea) py_str2ea; +%ignore execute_sync; +%ignore exec_request_t; +%rename (execute_sync) py_execute_sync; + +// Make askaddr(), askseg(), and asklong() return a +// tuple: (result, value) +%apply unsigned long *INOUT { sval_t *value }; +%rename (_asklong) asklong; +%apply unsigned long *INOUT { ea_t *addr }; +%rename (_askaddr) askaddr; +%apply unsigned long *INOUT { sel_t *sel }; +%rename (_askseg) askseg; + +%inline %{ +void refresh_lists(void) +{ + callui(ui_list); +} +%} + +%pythoncode %{ +def asklong(defval, format): + res, val = _idaapi._asklong(defval, format) + + if res == 1: + return val + else: + return None + +def askaddr(defval, format): + res, ea = _idaapi._askaddr(defval, format) + + if res == 1: + return ea + else: + return None + +def askseg(defval, format): + res, sel = _idaapi._askseg(defval, format) + + if res == 1: + return sel + else: + return None + +%} + +# This is for get_cursor() +%apply int *OUTPUT {int *x, int *y}; + +# This is for read_selection() +%apply unsigned long *OUTPUT { ea_t *ea1, ea_t *ea2 }; + +%{ +// + +//------------------------------------------------------------------------ +struct py_add_del_menu_item_ctx +{ + qstring menupath; + PyObject *cb_data; +}; + +//------------------------------------------------------------------------ +bool idaapi py_menu_item_callback(void *userdata) +{ + // userdata is a tuple of ( func, args ) + // func and args are borrowed references from userdata + PyObject *func = PyTuple_GET_ITEM(userdata, 0); + PyObject *args = PyTuple_GET_ITEM(userdata, 1); + + // call the python function + PyObject *result = PyEval_CallObject(func, args); + + // we cannot raise an exception in the callback, just print it. + if ( result == NULL ) + { + PyErr_Print(); + return false; + } + + bool ret = PyObject_IsTrue(result); + Py_DECREF(result); + return ret; +} + +//------------------------------------------------------------------------ +/* +# +def set_dock_pos(src, dest, orient, left = 0, top = 0, right = 0, bottom = 0) + """ + Sets the dock orientation of a window relatively to another window. + + @param src: Source docking control + @param dest: Destination docking control + @param orient: One of DOR_XXXX constants + @param left, top, right, bottom: These parameter if DOR_FLOATING is used, or if you want to specify the width of docked windows + @return: Boolean + + Example: + set_dock_pos('Structures', 'Enums', DOR_RIGHT) <- docks the Structures window to the right of Enums window + """ + pass +# +*/ + +//------------------------------------------------------------------------ +/* +int py_execute_sync(PyObject *py_callable, int reqf) +{ + if ( !PyCallable_Check(py_callable) ) + return -1; + + struct py_exec_request_t: exec_request_t + { + PyObject *py_callable; + virtual int idaapi execute(void) + { + PyObject *py_result = PyObject_CallFunctionObjArgs(py_callable, NULL); + int r = py_result == NULL || !PyInt_Check(py_result) ? -1 : PyInt_AsLong(py_result); + Py_XDECREF(py_result); + return r; + } + py_exec_request_t(PyObject *pyc): py_callable(pyc) { } + }; + py_exec_request_t req(py_callable); + return execute_sync(req, reqf); +} +*/ + + + +//------------------------------------------------------------------------ +// Some defines +#define POPUP_NAMES_COUNT 4 +#define MAX_CHOOSER_MENU_COMMANDS 10 +#define thisobj ((py_choose2_t *) obj) +#define thisdecl py_choose2_t *_this = thisobj +#define MENU_COMMAND_CB(id) static uint32 idaapi s_menu_command_##id(void *obj, uint32 n) { return thisobj->on_command(id, int(n)); } + +//------------------------------------------------------------------------ +// Helper functions +class py_choose2_t; +typedef std::map pychoose2_to_choose2_map_t; +static pychoose2_to_choose2_map_t choosers; + +py_choose2_t *choose2_find_instance(PyObject *self) +{ + pychoose2_to_choose2_map_t::iterator it = choosers.find(self); + if ( it == choosers.end() ) + return NULL; + return it->second; +} + +void choose2_add_instance(PyObject *self, py_choose2_t *c2) +{ + choosers[self] = c2; +} + +void choose2_del_instance(PyObject *self) +{ + pychoose2_to_choose2_map_t::iterator it = choosers.find(self); + if ( it != choosers.end() ) + choosers.erase(it); +} + +//------------------------------------------------------------------------ +class py_choose2_t +{ +private: + enum + { + CHOOSE2_HAVE_DEL = 0x0001, + CHOOSE2_HAVE_INS = 0x0002, + CHOOSE2_HAVE_UPDATE = 0x0004, + CHOOSE2_HAVE_EDIT = 0x0008, + CHOOSE2_HAVE_ENTER = 0x0010, + CHOOSE2_HAVE_GETICON = 0x0020, + CHOOSE2_HAVE_GETATTR = 0x0040, + CHOOSE2_HAVE_COMMAND = 0x0080, + CHOOSE2_HAVE_ONCLOSE = 0x0100 + }; + int flags; + int cb_flags; + qstring title; + PyObject *self; + qstrvec_t cols; + // the number of declarations should follow the MAX_CHOOSER_MENU_COMMANDS value + MENU_COMMAND_CB(0) MENU_COMMAND_CB(1) + MENU_COMMAND_CB(2) MENU_COMMAND_CB(3) + MENU_COMMAND_CB(4) MENU_COMMAND_CB(5) + MENU_COMMAND_CB(6) MENU_COMMAND_CB(7) + MENU_COMMAND_CB(8) MENU_COMMAND_CB(9) + static chooser_cb_t *menu_cbs[MAX_CHOOSER_MENU_COMMANDS]; + int menu_cb_idx; + //------------------------------------------------------------------------ + // Static methods to dispatch to member functions + //------------------------------------------------------------------------ + static int idaapi ui_cb(void *obj, int notification_code, va_list va) + { + if ( notification_code != ui_get_chooser_item_attrs ) + return 0; + va_arg(va, void *); + int n = int(va_arg(va, uint32)); + chooser_item_attrs_t *attr = va_arg(va, chooser_item_attrs_t *); + thisobj->on_get_line_attr(n, attr); + return 1; + } + static uint32 idaapi s_sizer(void *obj) + { + return (uint32)thisobj->on_get_size(); + } + static void idaapi s_getl(void *obj, uint32 n, char * const *arrptr) + { + thisobj->on_get_line(int(n), arrptr); + } + static uint32 idaapi s_del(void *obj, uint32 n) + { + return uint32(thisobj->on_delete_line(int(n))); + } + static void idaapi s_ins(void *obj) + { + thisobj->on_insert_line(); + } + static uint32 idaapi s_update(void *obj, uint32 n) + { + return uint32(thisobj->on_refresh(int(n))); + } + static void idaapi s_edit(void *obj, uint32 n) + { + thisobj->on_edit_line(int(n)); + } + static void idaapi s_enter(void * obj, uint32 n) + { + thisobj->on_select_line(int(n)); + } + static int idaapi s_get_icon(void *obj, uint32 n) + { + return thisobj->on_get_icon(int(n)); + } + static void idaapi s_destroy(void *obj) + { + thisobj->on_close(); + } +private: + //------------------------------------------------------------------------ + // Member functions corresponding to each chooser2() callback + //------------------------------------------------------------------------ + void on_get_line(int lineno, char * const *line_arr) + { + if ( lineno == 0 ) + { + for ( size_t i=0; i=0; i-- ) + line_arr[i][0] = '\0'; + + // Call Python + PyObject *list = PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1); + if ( list == NULL ) + return; + for ( int i=ncols-1; i>=0; i-- ) + { + PyObject *item = PyList_GetItem(list, Py_ssize_t(i)); + if ( item == NULL ) + continue; + const char *str = PyString_AsString(item); + if ( str != NULL ) + qstrncpy(line_arr[i], str, MAXSTR); + } + Py_DECREF(list); + } + + size_t on_get_size() + { + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL); + if ( pyres == NULL ) + return 0; + size_t res = PyInt_AsLong(pyres); + Py_DECREF(pyres); + return res; + } + + void on_close() + { +#ifdef CH_ATTRS + if ( (flags & CH_ATTRS) != 0 ) + unhook_from_notification_point(HT_UI, ui_cb, this); +#endif + // Call Python + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL); + Py_XDECREF(pyres); + Py_XDECREF(self); + + // Remove from list + choose2_del_instance(self); + + // delete this instance if none modal + if ( (flags & CH_MODAL) == 0 ) + delete this; +} + + int on_delete_line(int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_DELETE_LINE, "i", lineno - 1); + if ( pyres == NULL ) + return lineno; + size_t res = PyInt_AsLong(pyres); + Py_DECREF(pyres); + return res + 1; + } + + int on_refresh(int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_REFRESH, "i", lineno - 1); + if ( pyres == NULL ) + return lineno; + size_t res = PyInt_AsLong(pyres); + Py_DECREF(pyres); + return res + 1; + } + + void on_insert_line() + { + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL); + Py_XDECREF(pyres); + } + + void on_select_line(int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_SELECT_LINE, "i", lineno - 1); + Py_XDECREF(pyres); + } + + void on_edit_line(int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_EDIT_LINE, "i", lineno - 1); + Py_XDECREF(pyres); + } + + int on_command(int cmd_id, int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_COMMAND, "ii", lineno - 1, cmd_id); + if ( pyres==NULL ) + return lineno; + size_t res = PyInt_AsLong(pyres); + Py_XDECREF(pyres); + return res; + } + + int on_get_icon(int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_ICON, "i", lineno - 1); + size_t res = PyInt_AsLong(pyres); + Py_XDECREF(pyres); + return res; + } + void on_get_line_attr(int lineno, chooser_item_attrs_t *attr) + { + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1); + if ( pyres == NULL ) + return; + + if ( PyList_Check(pyres) ) + { + PyObject *item; + if ( (item = PyList_GetItem(pyres, 0)) != NULL ) + attr->color = PyInt_AsLong(item); + if ( (item = PyList_GetItem(pyres, 1)) != NULL ) + attr->flags = PyInt_AsLong(item); + } + Py_XDECREF(pyres); + } +public: + //------------------------------------------------------------------------ + // Public methods + //------------------------------------------------------------------------ + py_choose2_t() + { + flags = 0; + cb_flags = 0; + menu_cb_idx = 0; + self = NULL; + } + static py_choose2_t *find_chooser(const char *title) + { + return (py_choose2_t *) get_chooser_obj(title); + } + void close() + { + close_chooser(title.c_str()); + } + bool activate() + { + TForm *frm = find_tform(title.c_str()); + if ( frm == NULL ) + return false; + switchto_tform(frm, true); + return true; + } + + int choose2( + int fl, + int ncols, + const int *widths, + const char *title, + int deflt = -1, + // An array of 4 strings: ("Insert", "Delete", "Edit", "Refresh" + const char * const *popup_names = NULL, + int icon = -1, + int x1 = -1, int y1 = -1, int x2 = -1, int y2 = -1) + { + flags = fl; + if ( (flags & CH_ATTRS) != 0 ) + { + if ( !hook_to_notification_point(HT_UI, ui_cb, this) ) + flags &= ~CH_ATTRS; + } + this->title = title; + return ::choose2( + flags, + x1, y1, x2, y2, + this, + ncols, widths, + s_sizer, + s_getl, + title, + icon, + deflt, + cb_flags & CHOOSE2_HAVE_DEL ? s_del : NULL, + cb_flags & CHOOSE2_HAVE_INS ? s_ins : NULL, + cb_flags & CHOOSE2_HAVE_UPDATE ? s_update : NULL, + cb_flags & CHOOSE2_HAVE_EDIT ? s_edit : NULL, + cb_flags & CHOOSE2_HAVE_ENTER ? s_enter : NULL, + s_destroy, + popup_names, + cb_flags & CHOOSE2_HAVE_GETICON ? s_get_icon : NULL); + } + + int add_command(const char *caption, int flags=0, int menu_index=-1, int icon=-1) + { + if ( menu_cb_idx >= MAX_CHOOSER_MENU_COMMANDS ) + return -1; + bool ret = add_chooser_command(title.c_str(), caption, menu_cbs[menu_cb_idx], menu_index, icon, flags); + if ( !ret ) + return -1; + return menu_cb_idx++; + } + + int show(PyObject *self) + { + PyObject *attr; + // get title + if ( (attr = PyObject_TryGetAttrString(self, "title")) == NULL ) + return -1; + qstring title = PyString_AsString(attr); + Py_DECREF(attr); + + // get flags + if ( (attr = PyObject_TryGetAttrString(self, "flags")) == NULL ) + return -1; + int flags = PyInt_AsLong(attr); + Py_DECREF(attr); + + // get columns + if ( (attr = PyObject_TryGetAttrString(self, "cols")) == NULL ) + return -1; + + // get col count + int ncols = PyList_Size(attr); + + // get cols caption and widthes + intvec_t widths; + cols.qclear(); + for ( int i=0; iself = self; + + // Create chooser + int r = this->choose2(flags, ncols, &widths[0], title.c_str(), deflt, popup_names, icon, pts[0], pts[1], pts[2], pts[3]); + + // Clear temporary popup_names + if ( popup_names != NULL ) + { + for ( int i=0; iactivate(); + return 1; + } + c2 = new py_choose2_t(); + choose2_add_instance(self, c2); + return c2->show(self); +} + +//------------------------------------------------------------------------ +void choose2_close(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if ( c2 != NULL ) + c2->close(); +} + +//------------------------------------------------------------------------ +void choose2_refresh(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if ( c2 != NULL ) + c2->refresh(); +} + +//------------------------------------------------------------------------ +void choose2_activate(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if ( c2 != NULL ) + c2->activate(); +} + +//------------------------------------------------------------------------ +int choose2_add_command(PyObject *self, const char *caption, int flags=0, int menu_index=-1, int icon=-1) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if ( c2 != NULL ) + return c2->add_command(caption, flags, menu_index, icon); + else + return -2; +} + +//------------------------------------------------------------------------ +PyObject *choose2_find(const char *title) +{ + py_choose2_t *c2 = py_choose2_t::find_chooser(title); + if ( c2 == NULL ) + return NULL; + return c2->get_self(); +} +// + +%} + +#ifdef __NT__ +%{ +// +//--------------------------------------------------------------------------- +// Base class for all custviewer place_t providers +class custviewer_data_t +{ +public: + virtual void *get_ud() = 0; + virtual place_t *get_min() = 0; + virtual place_t *get_max() = 0; +}; + +//--------------------------------------------------------------------------- +class cvdata_simpleline_t: public custviewer_data_t +{ +private: + strvec_t lines; + simpleline_place_t pl_min, pl_max; +public: + + void *get_ud() + { + return &lines; + } + + place_t *get_min() + { + return &pl_min; + } + + place_t *get_max() + { + return &pl_max; + } + + strvec_t &get_lines() + { + return lines; + } + + void set_minmax(size_t start=0, size_t end=size_t(-1)) + { + if ( start == 0 && end == size_t(-1) ) + { + end = lines.size(); + pl_min.n = 0; + pl_max.n = end == 0 ? 0 : end - 1; + } + else + { + pl_min.n = start; + pl_max.n = end; + } + } + + bool set_line(size_t nline, simpleline_t &sl) + { + if ( nline >= lines.size() ) + return false; + lines[nline] = sl; + return true; + } + + bool del_line(size_t nline) + { + if ( nline >= lines.size() ) + return false; + lines.erase(lines.begin()+nline); + return true; + } + + void add_line(simpleline_t &line) + { + lines.push_back(line); + } + + void add_line(const char *str) + { + lines.push_back(simpleline_t(str)); + } + + bool insert_line(size_t nline, simpleline_t &line) + { + if ( nline >= lines.size() ) + return false; + lines.insert(lines.begin()+nline, line); + return true; + } + + bool patch_line(size_t nline, size_t offs, int value) + { + if ( nline >= lines.size() ) + return false; + qstring &L = lines[nline].line; + L[offs] = (uchar) value & 0xFF; + return true; + } + + const size_t to_lineno(place_t *pl) const + { + return ((simpleline_place_t *)pl)->n; + } + + bool curline(place_t *pl, size_t *n) + { + if ( pl == NULL ) + return false; + + *n = to_lineno(pl); + return true; + } + + simpleline_t *get_line(size_t nline) + { + return nline >= lines.size() ? NULL : &lines[nline]; + } + + simpleline_t *get_line(place_t *pl) + { + return pl == NULL ? NULL : get_line(((simpleline_place_t *)pl)->n); + } + + const size_t count() const + { + return lines.size(); + } + + void clear_lines() + { + lines.clear(); + set_minmax(); + } +}; + +//--------------------------------------------------------------------------- +class customviewer_t +{ +protected: + qstring _title; + TForm *_form; + TCustomControl *_cv; + custviewer_data_t *_data; + int _features; + enum + { + HAVE_HINT = 0x0001, + HAVE_KEYDOWN = 0x0002, + HAVE_POPUP = 0x0004, + HAVE_DBLCLICK = 0x0008, + HAVE_CURPOS = 0x0010, + HAVE_CLICK = 0x0020, + HAVE_CLOSE = 0x0040 + }; +private: + struct cvw_popupctx_t + { + size_t menu_id; + customviewer_t *cv; + cvw_popupctx_t(): menu_id(0), cv(NULL) { } + cvw_popupctx_t(size_t mid, customviewer_t *v): menu_id(mid), cv(v) { } + }; + typedef std::map cvw_popupmap_t; + static cvw_popupmap_t _global_popup_map; + static size_t _global_popup_id; + qstring _curline; + intvec_t _installed_popups; + + static bool idaapi s_popup_cb(void *ud) + { + customviewer_t *_this = (customviewer_t *)ud; + return _this->on_popup(); + } + + static bool idaapi s_popup_menu_cb(void *ud) + { + size_t mid = (size_t)ud; + cvw_popupmap_t::iterator it = _global_popup_map.find(mid); + if ( it == _global_popup_map.end() ) + return false; + return it->second.cv->on_popup_menu(it->second.menu_id); + } + + static bool idaapi s_cv_keydown(TCustomControl * /*cv*/, int vk_key, int shift, void *ud) + { + customviewer_t *_this = (customviewer_t *)ud; + return _this->on_keydown(vk_key, shift); + } + + // The popup menu is being constructed + static void idaapi s_cv_popup(TCustomControl * /*cv*/, void *ud) + { + customviewer_t *_this = (customviewer_t *)ud; + _this->on_popup(); + } + + // The user clicked + static bool idaapi s_cv_click(TCustomControl *cv, int shift, void *ud) + { + customviewer_t *_this = (customviewer_t *)ud; + return _this->on_click(shift); + } + + // The user double clicked + static bool idaapi s_cv_dblclick(TCustomControl * /*cv*/, int shift, void *ud) + { + customviewer_t *_this = (customviewer_t *)ud; + return _this->on_dblclick(shift); + } + + // Cursor position has been changed + static void idaapi s_cv_curpos(TCustomControl * /*cv*/, void *ud) + { + customviewer_t *_this = (customviewer_t *)ud; + _this->on_curpos_changed(); + } + + //-------------------------------------------------------------------------- + static int idaapi s_ui_cb(void *ud, int code, va_list va) + { + customviewer_t *_this = (customviewer_t *)ud; + switch ( code ) + { + case ui_get_custom_viewer_hint: + { + TCustomControl *viewer = va_arg(va, TCustomControl *); + place_t *place = va_arg(va, place_t *); + int *important_lines = va_arg(va, int *); + qstring &hint = *va_arg(va, qstring *); + return ((_this->_features & HAVE_HINT) == 0 || place == NULL || _this->_cv != viewer) ? 0 : (_this->on_hint(place, important_lines, hint) ? 1 : 0); + } + + case ui_tform_invisible: + { + TForm *form = va_arg(va, TForm *); + if ( _this->_form != form ) + break; + + unhook_from_notification_point(HT_UI, s_ui_cb, _this); + _this->on_close(); + _this->on_post_close(); + } + break; + } + + return 0; + } + + void on_post_close() + { + init_vars(); + clear_popup_menu(); + } + +public: + // + // All the overridable callbacks + // + + // OnClick + virtual bool on_click(int /*shift*/) { return false; } + + // OnDblClick + virtual bool on_dblclick(int /*shift*/) { return false; } + + // OnCurorPositionChanged + virtual void on_curpos_changed() { } + + // OnHostFormClose + virtual void on_close() { } + + // OnKeyDown + virtual bool on_keydown(int /*vk_key*/, int /*shift*/) { return false; } + + // OnPopupShow + virtual bool on_popup() { return false; } + + // OnHint + virtual bool on_hint(place_t * /*place*/, int * /*important_lines*/, qstring &/*hint*/) { return false; } + // OnPopupMenuClick + virtual bool on_popup_menu(size_t menu_id) { return false; } + + void init_vars() + { + _data = NULL; + _features = 0; + _curline.clear(); + _cv = NULL; + _form = NULL; + } + + customviewer_t() + { + init_vars(); + } + + ~customviewer_t() + { + } + + //-------------------------------------------------------------------------- + void close() + { + if ( _form != NULL ) + close_tform(_form, FORM_SAVE); + } + + //-------------------------------------------------------------------------- + bool set_range( + const place_t *minplace = NULL, + const place_t *maxplace = NULL) + { + if ( _cv == NULL ) + return false; + + set_custom_viewer_range( + _cv, + minplace == NULL ? _data->get_min() : minplace, + maxplace == NULL ? _data->get_max() : maxplace); + return true; + } + + place_t *get_place( + bool mouse = false, + int *x = 0, + int *y = 0) + { + return _cv == NULL ? NULL : get_custom_viewer_place(_cv, mouse, x, y); + } + + //-------------------------------------------------------------------------- + bool refresh() + { + if ( _cv == NULL ) + return false; + + refresh_custom_viewer(_cv); + return true; + } + + //-------------------------------------------------------------------------- + bool refresh_current() + { + int x, y; + place_t *pl = get_place(false, &x, &y); + if ( pl == NULL ) + return false; + + return jumpto(pl, x, y); + } + + //-------------------------------------------------------------------------- + bool get_current_word(bool mouse, qstring &word) + { + // query the cursor position + int x, y; + if ( get_place(mouse, &x, &y) == NULL ) + return false; + + // query the line at the cursor + const char *line = get_current_line(mouse, true); + if ( line == NULL ) + return false; + + if ( x >= (int)strlen(line) ) + return false; + + // find the beginning of the word + const char *ptr = line + x; + while ( ptr > line && !isspace(ptr[-1]) ) + ptr--; + + // find the end of the word + const char *begin = ptr; + ptr = line + x; + while ( !isspace(*ptr) && *ptr != '\0' ) + ptr++; + + word.qclear(); + word.append(begin, ptr-begin); + return true; + } + + //-------------------------------------------------------------------------- + const char *get_current_line(bool mouse, bool notags) + { + const char *r = get_custom_viewer_curline(_cv, mouse); + if ( r == NULL || !notags ) + return r; + + size_t sz = strlen(r); + if ( sz == 0 ) + return r; + + _curline.resize(sz + 5, '\0'); + tag_remove(r, &_curline[0], sz + 1); + return _curline.c_str(); + } + + //-------------------------------------------------------------------------- + bool is_focused() + { + return get_current_viewer() == _cv; + } + + //-------------------------------------------------------------------------- + bool jumpto(place_t *place, int x, int y) + { + return ::jumpto(_cv, place, x, y); + } + + //-------------------------------------------------------------------------- + void clear_popup_menu() + { + if ( _cv != NULL ) + set_custom_viewer_popup_menu(_cv, NULL); + + for (intvec_t::iterator it=_installed_popups.begin(), it_end=_installed_popups.end(); + it != it_end; + ++it) + { + _global_popup_map.erase(*it); + } + _installed_popups.clear(); + } + + //-------------------------------------------------------------------------- + size_t add_popup_menu( + const char *title, + const char *hotkey) + { + size_t menu_id = _global_popup_id + 1; + + // Overlap / already exists? + if (_cv == NULL || // No custviewer? + // Overlap? + menu_id == 0 || + // Already exists? + _global_popup_map.find(menu_id) != _global_popup_map.end()) + { + return 0; + } + add_custom_viewer_popup_item(_cv, title, hotkey, s_popup_menu_cb, (void *)menu_id); + + // Save global association + _global_popup_map[menu_id] = cvw_popupctx_t(menu_id, this); + _global_popup_id = menu_id; + + // Remember what menu IDs are set with this form + _installed_popups.push_back(menu_id); + return menu_id; + } + + //-------------------------------------------------------------------------- + bool create(const char *title, int features, custviewer_data_t *data) + { + // Already created? (in the instance) + if ( _form != NULL ) + return true; + + // Already created? (in IDA windows list) + HWND hwnd(NULL); + TForm *form = create_tform(title, &hwnd); + if ( hwnd == NULL ) + return false; + + _title = title; + _data = data; + _form = form; + _features = features; + + // Create the viewer + _cv = create_custom_viewer( + title, + (TWinControl *)_form, + _data->get_min(), + _data->get_max(), + _data->get_min(), + 0, + _data->get_ud()); + + // Set user-data + set_custom_viewer_handler(_cv, CVH_USERDATA, (void *)this); + + // + // Set other optional callbacks + // + if ( (features & HAVE_KEYDOWN) != 0 ) + set_custom_viewer_handler(_cv, CVH_KEYDOWN, (void *)s_cv_keydown); + + if ( (features & HAVE_POPUP) != 0 ) + set_custom_viewer_handler(_cv, CVH_POPUP, (void *)s_cv_popup); + + if ( (features & HAVE_DBLCLICK) != 0 ) + set_custom_viewer_handler(_cv, CVH_DBLCLICK, (void *)s_cv_dblclick); + + if ( (features & HAVE_CURPOS) != 0 ) + set_custom_viewer_handler(_cv, CVH_CURPOS, (void *)s_cv_curpos); + + if ( (features & HAVE_CLICK) != 0 ) + set_custom_viewer_handler(_cv, CVH_CLICK, (void *)s_cv_click); + + // Hook to UI notifications (for TForm close event) + hook_to_notification_point(HT_UI, s_ui_cb, this); + + return true; + } + + //-------------------------------------------------------------------------- + bool show() + { + // closed already? + if ( _form == NULL ) + return false; + + open_tform(_form, FORM_TAB|FORM_MENU|FORM_RESTORE); + int x, y; + get_place(false, &x, &y); + msg("curplace after open: %d %d\n", x, y); + return true; + } +}; + +customviewer_t::cvw_popupmap_t customviewer_t::_global_popup_map; +size_t customviewer_t::_global_popup_id = 0; +//--------------------------------------------------------------------------- +class py_simplecustview_t: public customviewer_t +{ +private: + cvdata_simpleline_t data; + PyObject *py_self, *py_this, *py_last_link; + int features; + + //-------------------------------------------------------------------------- + // Convert a tuple (String, [color, [bgcolor]]) to a simpleline_t + static bool py_to_simpleline(PyObject *py, simpleline_t &sl) + { + if ( PyString_Check(py) ) + { + sl.line = PyString_AsString(py); + return true; + } + Py_ssize_t sz; + if ( !PyTuple_Check(py) || (sz = PyTuple_Size(py)) <= 0 ) + return false; + PyObject *py_val = PyTuple_GetItem(py, 0); + if ( !PyString_Check(py_val) ) + return false; + sl.line = PyString_AsString(py_val); + + if ( (sz > 1) && (py_val = PyTuple_GetItem(py, 1)) && PyLong_Check(py_val) ) + sl.color = color_t(PyLong_AsUnsignedLong(py_val)); + + if ( (sz > 2) && (py_val = PyTuple_GetItem(py, 2)) && PyLong_Check(py_val) ) + sl.bgcolor = PyLong_AsUnsignedLong(py_val); + return true; + } + + // + // Callbacks + // + virtual bool on_click(int shift) + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift); + PyShowErr(S_ON_CLICK); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + //-------------------------------------------------------------------------- + // OnDblClick + virtual bool on_dblclick(int shift) + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift); + PyShowErr(S_ON_DBL_CLICK); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + //-------------------------------------------------------------------------- + // OnCurorPositionChanged + virtual void on_curpos_changed() + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL); + PyShowErr(S_ON_CURSOR_POS_CHANGED); + Py_XDECREF(py_result); + } + + //-------------------------------------------------------------------------- + // OnHostFormClose + virtual void on_close() + { + // Call the close method if it is there and the object is still bound + if ( (features & HAVE_CLOSE) != 0 && py_self != NULL ) + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL); + PyShowErr(S_ON_CLOSE); + Py_XDECREF(py_result); + + // Cleanup + Py_DECREF(py_self); + py_self = NULL; + } + } + + //-------------------------------------------------------------------------- + // OnKeyDown + virtual bool on_keydown(int vk_key, int shift) + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_KEYDOWN, "ii", vk_key, shift); + PyShowErr(S_ON_KEYDOWN); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + //-------------------------------------------------------------------------- +// OnPopupShow + virtual bool on_popup() + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_POPUP, NULL); + PyShowErr(S_ON_POPUP); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + //-------------------------------------------------------------------------- + // OnHint + virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) + { + size_t ln = data.to_lineno(place); + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_HINT, PY_FMT64, pyul_t(ln)); + PyShowErr(S_ON_HINT); + bool ok = py_result != NULL && PyTuple_Check(py_result) && PyTuple_Size(py_result) == 2; + if ( ok ) + { + // Borrow references + PyObject *py_nlines = PyTuple_GetItem(py_result, 0); + PyObject *py_hint = PyTuple_GetItem(py_result, 1); + + if ( important_lines != NULL ) + *important_lines = PyInt_AsLong(py_nlines); + hint = PyString_AsString(py_hint); + } + Py_XDECREF(py_result); + return ok; + } + + //-------------------------------------------------------------------------- + // OnPopupMenuClick + virtual bool on_popup_menu(size_t menu_id) + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_POPUP_MENU, PY_FMT64, pyul_t(menu_id)); + PyShowErr(S_ON_POPUP_MENU); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + //-------------------------------------------------------------------------- + void refresh_range() + { + data.set_minmax(); + set_range(); + } + +public: + py_simplecustview_t() + { + py_this = py_self = py_last_link = NULL; + } + ~py_simplecustview_t() + { + } + + //-------------------------------------------------------------------------- + // Edits an existing line + bool edit_line(size_t nline, PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + return data.set_line(nline, sl); + } + + // Low level: patches a line string directly + bool patch_line(size_t nline, size_t offs, int value) + { + return data.patch_line(nline, offs, value); + } + + // Insert a line + bool insert_line(size_t nline, PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + return data.insert_line(nline, sl); + } + + // Adds a line tuple + bool add_line(PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + data.add_line(sl); + refresh_range(); + return true; + } + + //-------------------------------------------------------------------------- + bool del_line(size_t nline) + { + bool ok = data.del_line(nline); + if ( ok ) + refresh_range(); + return ok; + } + + //-------------------------------------------------------------------------- + // Gets the position and returns a tuple (lineno, x, y) + PyObject *get_pos(bool mouse) + { + place_t *pl; + int x, y; + pl = get_place(mouse, &x, &y); + if ( pl == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("(" PY_FMT64 "ii)", pyul_t(data.to_lineno(pl)), x, y); + } + + //-------------------------------------------------------------------------- + // Returns the line tuple + PyObject *get_line(size_t nline) + { + simpleline_t *r = data.get_line(nline); + if ( r == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("(sII)", r->line.c_str(), (unsigned int)r->color, (unsigned int)r->bgcolor); + } + + // Returns the count of lines + const size_t count() const + { + return data.count(); + } + + // Clears lines + void clear() + { + data.clear_lines(); + refresh_range(); + } + + //-------------------------------------------------------------------------- + bool jumpto(size_t ln, int x, int y) + { + return customviewer_t::jumpto(&simpleline_place_t(ln), x, y); + } + + //-------------------------------------------------------------------------- + // Initializes and links the Python object to this class + bool init(PyObject *py_link, const char *title) + { + // Already created? + if ( _form != NULL ) + return true; + + // Probe callbacks + features = 0; + static struct + { + const char *cb_name; + int feature; + } const cbtable[] = + { + {S_ON_CLICK, HAVE_CLICK}, + {S_ON_CLOSE, HAVE_CLOSE}, + {S_ON_HINT, HAVE_HINT}, + {S_ON_KEYDOWN, HAVE_KEYDOWN}, + {S_ON_POPUP, HAVE_POPUP}, + {S_ON_DBL_CLICK, HAVE_DBLCLICK}, + {S_ON_CURSOR_POS_CHANGED, HAVE_CURPOS} + }; + for ( size_t i=0; i +%} +#endif + +#ifdef __NT__ +%inline %{ +// +// +// Pywraps Simple Custom Viewer functions +// +PyObject *pyscv_init(PyObject *py_link, const char *title) +{ + py_simplecustview_t *_this = new py_simplecustview_t(); + bool ok = _this->init(py_link, title); + if ( !ok ) + { + delete _this; + Py_RETURN_NONE; + } + return _this->get_pythis(); +} +#define DECL_THIS py_simplecustview_t *_this = py_simplecustview_t::get_this(py_this) + +//-------------------------------------------------------------------------- +bool pyscv_refresh(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->refresh(); +} + +//-------------------------------------------------------------------------- +bool pyscv_delete(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + _this->close(); + delete _this; + return true; +} + +//-------------------------------------------------------------------------- +bool pyscv_refresh_current(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->refresh_current(); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_get_current_line(PyObject *py_this, bool mouse, bool notags) +{ + DECL_THIS; + const char *line; + if ( _this == NULL || (line = _this->get_current_line(mouse, notags)) == NULL ) + Py_RETURN_NONE; + return PyString_FromString(line); +} + +//-------------------------------------------------------------------------- +bool pyscv_is_focused(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->is_focused(); +} + +void pyscv_clear_popup_menu(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->clear_popup_menu(); +} + +size_t pyscv_add_popup_menu(PyObject *py_this, const char *title, const char *hotkey) +{ + DECL_THIS; + return _this == NULL ? 0 : _this->add_popup_menu(title, hotkey); +} + +size_t pyscv_count(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? 0 : _this->count(); +} + +bool pyscv_show(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? false : _this->show(); +} + +void pyscv_close(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->close(); +} + +bool pyscv_jumpto(PyObject *py_this, size_t ln, int x, int y) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->jumpto(ln, x, y); +} + +// Returns the line tuple +PyObject *pyscv_get_line(PyObject *py_this, size_t nline) +{ + DECL_THIS; + if ( _this == NULL ) + Py_RETURN_NONE; + return _this->get_line(nline); +} + +//-------------------------------------------------------------------------- +// Gets the position and returns a tuple (lineno, x, y) +PyObject *pyscv_get_pos(PyObject *py_this, bool mouse) +{ + DECL_THIS; + if ( _this == NULL ) + Py_RETURN_NONE; + return _this->get_pos(mouse); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_clear_lines(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->clear(); + Py_RETURN_NONE; +} + +//-------------------------------------------------------------------------- +// Adds a line tuple +bool pyscv_add_line(PyObject *py_this, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->add_line(py_sl); +} + +//-------------------------------------------------------------------------- +bool pyscv_insert_line(PyObject *py_this, size_t nline, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->insert_line(nline, py_sl); +} + +//-------------------------------------------------------------------------- +bool pyscv_patch_line(PyObject *py_this, size_t nline, size_t offs, int value) +{ + DECL_THIS; + return _this == NULL ? false : _this->patch_line(nline, offs, value); +} + +//-------------------------------------------------------------------------- +bool pyscv_del_line(PyObject *py_this, size_t nline) +{ + DECL_THIS; + return _this == NULL ? false : _this->del_line(nline); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_get_selection(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + Py_RETURN_NONE; + return _this->py_get_selection(); +} + +//-------------------------------------------------------------------------- +PyObject *pyscv_get_current_word(PyObject *py_this, bool mouse) +{ + DECL_THIS; + if ( _this != NULL ) + { + qstring word; + if ( _this->get_current_word(mouse, word) ) + return PyString_FromString(word.c_str()); + } + Py_RETURN_NONE; +} + +//-------------------------------------------------------------------------- +// Edits an existing line +bool pyscv_edit_line(PyObject *py_this, size_t nline, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->edit_line(nline, py_sl); +} +#undef DECL_THIS +// +%} +#endif + +%inline %{ +uint32 idaapi choose_sizer(void *self) +{ + PyObject *pyres; + uint32 res; + + pyres = PyObject_CallMethod((PyObject *)self, "sizer", ""); + res = PyInt_AsLong(pyres); + Py_DECREF(pyres); + return res; +} + +char * idaapi choose_getl(void *self, uint32 n, char *buf) +{ + PyObject *pyres; + char *res; + + pyres = PyObject_CallMethod((PyObject *)self, "getl", "l", n); + + if (!pyres) + { + strcpy(buf, ""); + return buf; + } + + res = PyString_AsString(pyres); + + if (res) + { + strncpy(buf, res, MAXSTR); + res = buf; + } + else + { + strcpy(buf, ""); + res = buf; + } + + Py_DECREF(pyres); + return res; +} + +void idaapi choose_enter(void *self, uint32 n) +{ + PyObject_CallMethod((PyObject *)self, "enter", "l", n); + return; +} + +uint32 choose_choose(void *self, + int flags, + int x0,int y0, + int x1,int y1, + int width) +{ + PyObject *pytitle; + const char *title; + if ((pytitle = PyObject_GetAttrString((PyObject *)self, "title"))) + { + title = PyString_AsString(pytitle); + } + else + { + title = "Choose"; + pytitle = NULL; + } + int r = choose( + flags, + x0, y0, + x1, y1, + self, + width, + &choose_sizer, + &choose_getl, + title, + 1, + 1, + NULL, /* del */ + NULL, /* inst */ + NULL, /* update */ + NULL, /* edit */ + &choose_enter, + NULL, /* destroy */ + NULL, /* popup_names */ + NULL /* get_icon */ + ); + Py_XDECREF(pytitle); + return r; +} + +// +//------------------------------------------------------------------------ + + + +//------------------------------------------------------------------------ +/* +# +def asktext(max_text, defval, prompt): + """ + Asks for a long text + + @param max_text: Maximum text length + @param defval: The default value + @param prompt: The prompt value + @return: None or the entered string + """ + pass +# +*/ +PyObject *py_asktext(int max_text, const char *defval, const char *prompt) +{ + if ( max_text <= 0 ) + Py_RETURN_NONE; + + char *buf = new char[max_text]; + if ( buf == NULL ) + Py_RETURN_NONE; + + PyObject *py_ret; + if ( asktext(size_t(max_text), buf, defval, prompt) != NULL ) + { + py_ret = PyString_FromString(buf); + } + else + { + py_ret = Py_None; + Py_INCREF(py_ret); + } + delete [] buf; + return py_ret; +} + +//------------------------------------------------------------------------ +/* +# +def str2ea(addr): + """ + Converts a string express to EA. The expression evaluator may be called as well. + + @return: BADADDR or address value + """ + pass +# +*/ +ea_t py_str2ea(const char *str, ea_t screenEA = BADADDR) +{ + ea_t ea; + bool ok = str2ea(str, &ea, screenEA); + return ok ? ea : BADADDR; +} + +//------------------------------------------------------------------------ +/* +# +def del_menu_item(menu_ctx): + """ + Deletes a menu item previously added with add_menu_item() + + @param menu_ctx: value returned by add_menu_item() + @return: Boolean + """ + pass +# +*/ +static bool py_del_menu_item(PyObject *py_ctx) +{ + if ( !PyCObject_Check(py_ctx) ) + return false; + + py_add_del_menu_item_ctx *ctx = (py_add_del_menu_item_ctx *)PyCObject_AsVoidPtr(py_ctx); + + bool ok = del_menu_item(ctx->menupath.c_str()); + + if ( ok ) + { + Py_DECREF(ctx->cb_data); + delete ctx; + } + + return ok; +} + +//------------------------------------------------------------------------ +/* +# +def add_menu_item(menupath, name, hotkey, flags, callback, args): + """ + Adds a menu item + + @param menupath: path to the menu item after or before which the insertion will take place + @param name: name of the menu item (~x~ is used to denote Alt-x hot letter) + @param hotkey: hotkey for the menu item (may be empty) + @param flags: one of SETMENU_... consts + @callback: function which gets called when the user selects the menu item. + The function callback is of the form: + def callback(*args): + pass + @param: args: tuple containing the arguments + + @return: None or a menu context (to be used by del_menu_item()) + """ + pass +# +*/ +static PyObject *py_add_menu_item( + const char *menupath, + const char *name, + const char *hotkey, + int flags, + PyObject *pyfunc, + PyObject *args) +{ + if ( !PyTuple_Check(args) ) + { + PyErr_SetString(PyExc_TypeError, "args must be a tuple or None"); + return NULL; + } + + PyObject *cb_data = Py_BuildValue("(OO)", pyfunc, args); + bool b = add_menu_item(menupath, name, hotkey, flags, py_menu_item_callback, (void *)cb_data); + if ( !b ) + { + Py_XDECREF(cb_data); + Py_RETURN_NONE; + } + py_add_del_menu_item_ctx *ctx = new py_add_del_menu_item_ctx(); + ctx->menupath = menupath; + ctx->menupath.append(name); + ctx->cb_data = cb_data; + return PyCObject_FromVoidPtr(ctx, NULL); +} + +//------------------------------------------------------------------------ +/* +# + +MFF_FAST = 0x0000 +"""execute code as soon as possible +this mode is ok call ui related functions +that do not query the database.""" + +MFF_READ = 0x0001 +"""execute code only when ida is idle and it is safe to query the database. +this mode is recommended only for code that does not modify the database. +(nb: ida may be in the middle of executing another user request, for example it may be waiting for him to enter values into a modal dialog box)""" + +MFF_WRITE = 0x0002 +"""execute code only when ida is idle and it is safe to modify the database. in particular, this flag will suspend execution if there is +a modal dialog box on the screen this mode can be used to call any ida api function. MFF_WRITE implies MFF_READ""" + +def py_execute_sync(callable, reqf) + """ + Converts a string express to EA. The expression evaluator may be called as well. + + @param callable: A python callable object + @param reqf: one of MFF_ flags + @return: BADADDR or address value + """ + pass +# +*/ +//int py_execute_sync(PyObject *py_callable, int reqf); + + + +PyObject *choose2_find(const char *title); +int choose2_add_command(PyObject *self, const char *caption, int flags, int menu_index, int icon); +void choose2_refresh(PyObject *self); +void choose2_close(PyObject *self); +int choose2_show(PyObject *self); +void choose2_activate(PyObject *self); +// +%} + +%include "kernwin.hpp" +uint32 choose_choose(PyObject *self, + int flags, + int x0,int y0, + int x1,int y1, + int width); + + + + +%pythoncode %{ + +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 & 1) == 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 + """ + return _idaapi.choose_choose(self, self.flags, self.x0, self.y0, self.x1, self.y1, self.width) + +# +DP_LEFT = 0x0001 +DP_TOP = 0x0002 +DP_RIGHT = 0x0004 +DP_BOTTOM = 0x0008 +DP_INSIDE = 0x0010 +# if not before, then it is after +# (use DP_INSIDE | DP_BEFORE to insert a tab before a given tab) +# this flag alone cannot be used to determine orientation +DP_BEFORE = 0x0020 +# used with combination of other flags +DP_RAW = 0x0040 +DP_FLOATING = 0x0080 + + + +class Choose2(object): + """ + Choose2 wrapper class. + + Some constants are defined in this class. Please refer to kernwin.hpp for more information. + """ + + CH_MODAL = 0x01 + CH_MULTI = 0x02 + CH_MULTI_EDIT = 0x04 + CH_NOBTNS = 0x08 + CH_ATTRS = 0x10 + CH_BUILTIN_MASK = 0xF80000 + + # column flags (are specified in the widths array) + CHCOL_PLAIN = 0x00000000 + CHCOL_PATH = 0x00010000 + CHCOL_HEX = 0x00020000 + CHCOL_DEC = 0x00030000 + CHCOL_FORMAT = 0x00070000 + + def __init__(self, title, cols, flags=0, popup_names=None, icon=-1, x1=-1, y1=-1, x2=-1, y2=-1, deflt=-1): + """Constructs a chooser window. + @param title: The chooser title + @param cols: a list of colums; each list item is a list of two items + example: [ ["Address", 10 | Choose2.CHCOL_HEX], ["Name", 30 | CHCOL_PLAIN] ] + @param flags: One of CH_XXXX constants + @param deflt: Default starting item + @param popup_names: list of new captions to replace this list ["Insert", "Delete", "Edit", "Refresh"] + @param icon: Icon index (the icon should exist in ida resources) + @param x1, y1, x2, y2: The default location + """ + self.title = title + self.flags = flags + self.cols = cols + self.deflt = deflt + self.popup_names = popup_names + self.icon = icon + self.x1 = x1 + self.y1 = y1 + self.x2 = x2 + self.y2 = y2 + + def Show(self, modal=False): + """Activates or creates a chooser window""" + if modal: + self.flags |= Choose2.CH_MODAL + else: + self.flags &= ~Choose2.CH_MODAL + return _idaapi.choose2_show(self) + + def Activate(self): + """Activates a visible chooser""" + return _idaapi.choose2_activate(self) + + def Refresh(self): + """Causes the refresh callback to trigger""" + return _idaapi.choose2_refresh(self) + + def Close(self): + """Closes the chooser""" + return _idaapi.choose2_close(self) + + def AddCommand(self, caption, flags = _idaapi.CHOOSER_POPUP_MENU, menu_index=-1,icon = -1): + """Adds a new chooser command + Save the returned value and later use it in the OnCommand handler + + @return: Returns a negative value on failure or the command index + """ + return _idaapi.choose2_add_command(self, caption, flags, menu_index, icon) + + # + # Implement these methods in the subclass: + # +# +# def OnClose(self): +# """ +# Called when the window is being closed. +# This callback is mandatory. +# @return: nothing +# """ +# pass +# +# def OnGetLine(self, n): +# """Called when the chooser window requires lines. +# This callback is mandatory. +# @return: The user should return a list with ncols elements. +# example: a list [col1, col2, col3, ...] describing the n-th line +# """ +# return ["col1 val", "col2 val"] +# +# def OnGetSize(self): +# """Returns the element count. +# This callback is mandatory. +# @return: Number of elements +# """ +# return len(self.the_list) +# +# def OnEditLine(self, n): +# """ +# Called when an item is being edited. +# @param n: Line number (zero based) +# @return: Nothing +# """ +# pass +# +# def OnInsertLine(self): +# """Called when 'Insert' is selected either via the hotkey or popup menu. +# @return: Nothing +# """ +# pass +# +# def OnSelectLine(self, n): +# """Called when the line selection changes""" +# pass +# +# def OnDeleteLine(self, n): +# """Called when a line is about to be deleted""" +# return self.n +# +# def OnRefresh(self, n): +# """Called when the 'Refresh' is selected +# @return: Return the number of elements +# """ +# return self.n +# +# def OnCommand(self, n, cmd_id): +# """Return int ; check add_chooser_command()""" +# return 0 +# +# def OnGetIcon(self, n): +# """Return icon number for a given item (or -1 if no icon is avail)""" +# return -1 +# +# def OnGetLineAttr(self, n): +# """Return list [bgcolor, flags=CHITEM_XXXX] or None; check chooser_item_attrs_t""" +# return [0x0, CHITEM_BOLD] +# +# +%} + +#ifdef __NT__ +%pythoncode %{ +# +class simplecustviewer_t(object): + """The base class for implementing simple custom viewers""" + def __init__(self): + self.__this = None + + def __del__(self): + """Destructor. It also frees the associated C++ object""" + try: + _idaapi.pyscv_delete(self.__this) + except: + pass + + @staticmethod + def __make_sl_arg(line, fgcolor=None, bgcolor=None): + return line if (fgcolor is None and bgcolor is None) else (line, fgcolor, bgcolor) + + def Create(self, title): + """ + Creates the custom view. This should be the first method called after instantiation + + @param title: The title of the view + @return: Boolean whether it succeeds or fails. It may fail if a window with the same title is already open. + In this case better close existing windows + """ + self.title = title + self.__this = _idaapi.pyscv_init(self, title) + return True if self.__this else False + + def Close(self): + """ + Destroys the view. + One has to call Create() afterwards. + Show() can be called and it will call Create() internally. + @return: Boolean + """ + return _idaapi.pyscv_close(self.__this) + + def Show(self): + """ + Shows an already created view. It the view was close, then it will call Create() for you + @return: Boolean + """ + return _idaapi.pyscv_show(self.__this) + + def Refresh(self): + return _idaapi.pyscv_refresh(self.__this) + + def RefreshCurrent(self): + """Refreshes the current line only""" + return _idaapi.pyscv_refresh_current(self.__this) + + def Count(self): + """Returns the number of lines in the view""" + return _idaapi.pyscv_count(self.__this) + + def GetSelection(self): + """ + Returns the selected area or None + @return: + - tuple(x1, y1, x2, y2) + - None if no selection + """ + return _idaapi.pyscv_get_selection(self.__this) + + def ClearLines(self): + """Clears all the lines""" + _idaapi.pyscv_clear_lines(self.__this) + + def AddLine(self, line, fgcolor=None, bgcolor=None): + """ + Adds a colored line to the view + @return: Boolean + """ + return _idaapi.pyscv_add_line(self.__this, self.__make_sl_arg(line, fgcolor, bgcolor)) + + def InsertLine(self, lineno, line, fgcolor=None, bgcolor=None): + """ + Inserts a line in the given position + @return Boolean + """ + return _idaapi.pyscv_insert_line(self.__this, lineno, self.__make_sl_arg(line, fgcolor, bgcolor)) + + def EditLine(self, lineno, line, fgcolor=None, bgcolor=None): + """ + Edits an existing line. + @return Boolean + """ + return _idaapi.pyscv_edit_line(self.__this, lineno, self.__make_sl_arg(line, fgcolor, bgcolor)) + + def PatchLine(self, lineno, offs, value): + """Patches an existing line character at the given offset. This is a low level function. You must know what you're doing""" + return _idaapi.pyscv_patch_line(self.__this, lineno, offs, value) + + def DelLine(self, lineno): + """ + Deletes an existing line + @return Boolean + """ + return _idaapi.pyscv_del_line(self.__this, lineno) + + def GetLine(self, lineno): + """ + Returns a line + @param lineno: The line number + @return: + Returns a tuple (colored_line, fgcolor, bgcolor) or None + """ + return _idaapi.pyscv_get_line(self.__this, lineno) + + def GetCurrentWord(self, mouse = 0): + """ + Returns the current word + @param mouse: Use mouse position or cursor position + @return: None if failed or a String containing the current word at mouse or cursor + """ + return _idaapi.pyscv_get_current_word(self.__this, mouse) + + def GetCurrentLine(self, mouse = 0, notags = 0): + """ + Returns the current line. + @param mouse: Current line at mouse pos + @param notags: If True then tag_remove() will be called before returning the line + @return: Returns the current line (colored or uncolored) or None on failure + """ + return _idaapi.pyscv_get_current_line(self.__this, mouse, notags) + + def GetPos(self, mouse = 0): + """ + Returns the current cursor or mouse position. + @param mouse: return mouse position + @return: Returns a tuple (lineno, x, y) + """ + return _idaapi.pyscv_get_pos(self.__this, mouse) + + def GetLineNo(self, mouse = 0): + """Calls GetPos() and returns the current line number only or None on failure""" + r = self.GetPos(mouse) + return None if not r else r[0] + + def Jump(self, lineno, x=0, y=0): + return _idaapi.pyscv_jumpto(self.__this, lineno, x, y) + + def AddPopupMenu(self, title, hotkey=""): + """ + Adds a popup menu item + @param title: The name of the menu item + @param hotkey: Hotkey of the item or just empty + @return: Returns the + """ + return _idaapi.pyscv_add_popup_menu(self.__this, title, hotkey) + + def ClearPopupMenu(self): + """ + Clears all previously installed popup menu items. + Use this function if you're generating menu items on the fly (in the OnPopup() callback), + and before adding new items + """ + _idaapi.pyscv_clear_popup_menu(self.__this) + + def IsFocused(self): + """Returns True if the current view is the focused view""" + return _idaapi.pyscv_is_focused(self.__this) + + # Here are all the supported events +# +# def OnClick(self, shift): +# """ +# User clicked in the view +# @param shift: Shift flag +# @return: Boolean. True if you handled the event +# """ +# print "OnClick, shift=%d" % shift +# return True +# +# def OnDblClick(self, shift): +# """ +# User dbl-clicked in the view +# @param shift: Shift flag +# @return: Boolean. True if you handled the event +# """ +# print "OnDblClick, shift=%d" % shift +# return True +# +# def OnCursorPosChanged(self): +# """ +# Cursor position changed. +# @return: Nothing +# """ +# print "OnCurposChanged" +# +# def OnClose(self): +# """ +# The view is closing. Use this event to cleanup. +# @return: Nothing +# """ +# print "OnClose" +# +# 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 +# """ +# print "OnKeydown, vk=%d shift=%d" % (vkey, shift) +# return False +# +# def OnPopup(self): +# """ +# Context menu popup is about to be shown. Create items dynamically if you wish +# @return: Boolean. True if you handled the event +# """ +# print "OnPopup" +# +# def OnHint(self, lineno): +# """ +# Hint requested for the given line number. +# @param lineno: The line number (zero based) +# @return: +# - tuple(number of important lines, hint string) +# - None: if no hint available +# """ +# return (1, "OnHint, line=%d" % lineno) +# +# def OnPopupMenu(self, menu_id): +# """ +# A context (or popup) menu item was executed. +# @param menu_id: ID previously registered with add_popup_menu() +# @return: Boolean +# """ +# print "OnPopupMenu, menu_id=" % menu_id +# return True +# +# +%} +#endif diff --git a/swig/lines.i b/swig/lines.i index 25feafe..15885f3 100644 --- a/swig/lines.i +++ b/swig/lines.i @@ -1,186 +1,225 @@ -// FIXME: These should be fixed -%ignore requires_color_esc; -%ignore tag_on; -%ignore tag_remove; -%ignore tag_off; -%ignore tag_addchr; -%ignore tag_addstr; -%ignore tag_addr; -%ignore tag_advance; -%ignore tag_skipcodes; -%ignore tag_skipcode; -%ignore set_user_defined_prefix; -%ignore get_user_defined_prefix; -// Ignore va_list versions -%ignore printf_line_v; -%ignore gen_colored_cmt_line_v; -%ignore gen_cmt_line_v; -%ignore add_long_cmt_v; -%ignore describex; -// Kernel-only and unexported symbols -%ignore init_sourcefiles; -%ignore save_sourcefiles; -%ignore term_sourcefiles; -%ignore move_sourcefiles; -%ignore gen_xref_lines; -%ignore ml_getcmt_t; -%ignore ml_getnam_t; -%ignore ml_genxrf_t; -%ignore ml_saver_t; -%ignore setup_makeline; -%ignore MAKELINE_NONE; -%ignore MAKELINE_BINPREF; -%ignore MAKELINE_VOID; -%ignore MAKELINE_STACK; -%ignore save_line_in_array; -%ignore init_lines_array; -%ignore finish_makeline; -%ignore generate_disassembly; -%ignore gen_labeled_line; -%ignore gen_lname_line; -%ignore makeline_producer_t; -%ignore set_makeline_producer; -%ignore closing_comment; -%ignore close_comment; -%ignore copy_extra_lines; -%ignore ExtraLines; -%ignore ExtraKill; -%ignore ExtraFree; -%ignore Dumper; -%ignore init_lines; -%ignore save_lines; -%ignore term_lines; -%ignore gl_namedone; -%ignore data_as_stack; -%ignore calc_stack_alignment; -%ignore align_down_to_stack; -%ignore align_up_to_stack; -%ignore remove_spaces; - -%include "lines.hpp" - -%rename (generate_disassembly) py_generate_disassembly; -%rename (tag_remove) py_tag_remove; -%rename (tag_addr) py_tag_addr; -%rename (tag_skipcodes) py_tag_skipcodes; -%rename (tag_skipcode) py_tag_skipcode; -%rename (tag_advance) py_tag_advance; -%rename (generate_disassembly) py_generate_disassembly; - -%inline -{ -// -//------------------------------------------------------------------------- -PyObject *py_tag_remove(const char *instr) -{ - size_t sz = strlen(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 ) - { - Py_INCREF(Py_None); - res = Py_None; - } - else - { - res = PyString_FromString(buf); - } - delete [] buf; - return res; -} - -//------------------------------------------------------------------------- -PyObject *py_tag_addr(ea_t ea) -{ - char buf[100]; - tag_addr(buf, buf + sizeof(buf), ea); - return PyString_FromString(buf); -} - -//------------------------------------------------------------------------- -int py_tag_skipcode(const char *line) -{ - return tag_skipcode(line)-line; -} - -//------------------------------------------------------------------------- -int py_tag_skipcodes(const char *line) -{ - return tag_skipcodes(line)-line; -} - -//------------------------------------------------------------------------- -int py_tag_advance(const char *line, int cnt) -{ - return tag_advance(line, cnt)-line; -} - -//------------------------------------------------------------------------- -PyObject *py_generate_disassembly(ea_t ea, int max_lines, bool as_stack, bool notags) -{ - if ( max_lines <= 0 ) - Py_RETURN_NONE; - - qstring qbuf; - char **lines = new char *[max_lines]; - int lnnum; - int nlines = generate_disassembly(ea, lines, max_lines, &lnnum, as_stack); - - PyObject *py_tuple = PyTuple_New(nlines); - for ( int i=0; i - -} - -%pythoncode %{ -# - -# ---------------- Color escape sequence defitions ------------------------- -COLOR_ADDR_SIZE = 16 if _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL else 8 -SCOLOR_FG_MAX = '\x28' # Max color number -SCOLOR_OPND1 = chr(cvar.COLOR_ADDR+1) # Instruction operand 1 -SCOLOR_OPND2 = chr(cvar.COLOR_ADDR+2) # Instruction operand 2 -SCOLOR_OPND3 = chr(cvar.COLOR_ADDR+3) # Instruction operand 3 -SCOLOR_OPND4 = chr(cvar.COLOR_ADDR+4) # Instruction operand 4 -SCOLOR_OPND5 = chr(cvar.COLOR_ADDR+5) # Instruction operand 5 -SCOLOR_OPND6 = chr(cvar.COLOR_ADDR+6) # Instruction operand 6 -SCOLOR_UTF8 = chr(cvar.COLOR_ADDR+10) # Following text is UTF-8 encoded - -# ---------------- Line prefix colors -------------------------------------- -PALETTE_SIZE = (cvar.COLOR_FG_MAX+_idaapi.COLOR_BG_MAX) - -def requires_color_esc(c): - """ - Checks if the given character requires escaping - @param c: character (string of one char) - @return: Boolean - """ - t = ord(c[0]) - return c >= COLOR_ON and c <= COLOR_INV - -def COLSTR(str,tag): - return SCOLOR_ON + tag + str + SCOLOR_OFF + tag - -# - +// FIXME: These should be fixed +%ignore requires_color_esc; +%ignore tag_on; +%ignore tag_remove; +%ignore tag_off; +%ignore tag_addchr; +%ignore tag_addstr; +%ignore tag_addr; +%ignore tag_advance; +%ignore tag_skipcodes; +%ignore tag_skipcode; +%ignore set_user_defined_prefix; +%ignore get_user_defined_prefix; +// Ignore va_list versions +%ignore printf_line_v; +%ignore gen_colored_cmt_line_v; +%ignore gen_cmt_line_v; +%ignore add_long_cmt_v; +%ignore describex; +// Kernel-only and unexported symbols +%ignore init_sourcefiles; +%ignore save_sourcefiles; +%ignore term_sourcefiles; +%ignore move_sourcefiles; +%ignore gen_xref_lines; +%ignore ml_getcmt_t; +%ignore ml_getnam_t; +%ignore ml_genxrf_t; +%ignore ml_saver_t; +%ignore setup_makeline; +%ignore MAKELINE_NONE; +%ignore MAKELINE_BINPREF; +%ignore MAKELINE_VOID; +%ignore MAKELINE_STACK; +%ignore save_line_in_array; +%ignore init_lines_array; +%ignore finish_makeline; +%ignore generate_disassembly; +%ignore gen_labeled_line; +%ignore gen_lname_line; +%ignore makeline_producer_t; +%ignore set_makeline_producer; +%ignore closing_comment; +%ignore close_comment; +%ignore copy_extra_lines; +%ignore ExtraLines; +%ignore ExtraKill; +%ignore ExtraFree; +%ignore Dumper; +%ignore init_lines; +%ignore save_lines; +%ignore term_lines; +%ignore gl_namedone; +%ignore data_as_stack; +%ignore calc_stack_alignment; +%ignore align_down_to_stack; +%ignore align_up_to_stack; +%ignore remove_spaces; +%ignore bgcolors; + +%include "lines.hpp" + +%rename (generate_disassembly) py_generate_disassembly; +%rename (tag_remove) py_tag_remove; +%rename (tag_addr) py_tag_addr; +%rename (tag_skipcodes) py_tag_skipcodes; +%rename (tag_skipcode) py_tag_skipcode; +%rename (tag_advance) py_tag_advance; +%rename (generate_disassembly) py_generate_disassembly; + +%inline %{ +// +//------------------------------------------------------------------------- +/* +# +def tag_remove(colstr): + """ + Remove color escape sequences from a string + @param colstr: the colored string with embedded tags + @return: + None on failure + or a new string w/o the tags + """ + pass +# +*/ +PyObject *py_tag_remove(const char *instr) +{ + size_t sz = strlen(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 ) + { + Py_INCREF(Py_None); + res = Py_None; + } + else + { + res = PyString_FromString(buf); + } + delete [] buf; + return res; +} + +//------------------------------------------------------------------------- +PyObject *py_tag_addr(ea_t ea) +{ + char buf[100]; + tag_addr(buf, buf + sizeof(buf), ea); + return PyString_FromString(buf); +} + +//------------------------------------------------------------------------- +int py_tag_skipcode(const char *line) +{ + return tag_skipcode(line)-line; +} + +//------------------------------------------------------------------------- +int py_tag_skipcodes(const char *line) +{ + return tag_skipcodes(line)-line; +} + +//------------------------------------------------------------------------- +int py_tag_advance(const char *line, int cnt) +{ + return tag_advance(line, cnt)-line; +} + +//------------------------------------------------------------------------- +/* +# +def generate_disassembly(ea, max_lines, as_stack, notags): + """ + Generate disassembly lines (many lines) and put them into a buffer + + @param ea: address to generate disassembly for + @param max_lines: how many lines max to generate + @param as_stack: Display undefined items as 2/4/8 bytes + @return: + - None on failure + - tuple(most_important_line_number, tuple(lines)) : Returns a tuple containing + the most important line number and a tuple of generated lines + """ + pass +# +*/ +PyObject *py_generate_disassembly( + ea_t ea, + int max_lines, + bool as_stack, + bool notags) +{ + if ( max_lines <= 0 ) + Py_RETURN_NONE; + + qstring qbuf; + char **lines = new char *[max_lines]; + int lnnum; + int nlines = generate_disassembly(ea, lines, max_lines, &lnnum, as_stack); + + PyObject *py_tuple = PyTuple_New(nlines); + for ( int i=0; i + +%} + +%pythoncode %{ +# + +# ---------------- Color escape sequence defitions ------------------------- +COLOR_ADDR_SIZE = 16 if _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL else 8 +SCOLOR_FG_MAX = '\x28' # Max color number +SCOLOR_OPND1 = chr(cvar.COLOR_ADDR+1) # Instruction operand 1 +SCOLOR_OPND2 = chr(cvar.COLOR_ADDR+2) # Instruction operand 2 +SCOLOR_OPND3 = chr(cvar.COLOR_ADDR+3) # Instruction operand 3 +SCOLOR_OPND4 = chr(cvar.COLOR_ADDR+4) # Instruction operand 4 +SCOLOR_OPND5 = chr(cvar.COLOR_ADDR+5) # Instruction operand 5 +SCOLOR_OPND6 = chr(cvar.COLOR_ADDR+6) # Instruction operand 6 +SCOLOR_UTF8 = chr(cvar.COLOR_ADDR+10) # Following text is UTF-8 encoded + +# ---------------- Line prefix colors -------------------------------------- +PALETTE_SIZE = (cvar.COLOR_FG_MAX+_idaapi.COLOR_BG_MAX) + +def requires_color_esc(c): + """ + Checks if the given character requires escaping + @param c: character (string of one char) + @return: Boolean + """ + t = ord(c[0]) + return c >= COLOR_ON and c <= COLOR_INV + +def COLSTR(str, tag): + """ + Utility function to create a colored line + @param str: The string + @param tag: Color tag constant. One of SCOLOR_XXXX + """ + return SCOLOR_ON + tag + str + SCOLOR_OFF + tag + +# + %} \ No newline at end of file diff --git a/swig/loader.i b/swig/loader.i index a4afaeb..6971548 100644 --- a/swig/loader.i +++ b/swig/loader.i @@ -72,12 +72,14 @@ %ignore save_fileregions; %ignore add_fileregion; %ignore move_fileregions; +%ignore del_fileregions; %ignore local_gen_idc_file; %ignore print_all_places; %ignore save_text_line; %ignore print_all_structs; %ignore print_all_enums; %ignore enum_processor_modules; +%ignore enum_plugins; %ignore database_id0; %ignore is_database_ext; %ignore ida_database_memory; diff --git a/swig/nalt.i b/swig/nalt.i index cc8a3cf..eb14cc6 100644 --- a/swig/nalt.i +++ b/swig/nalt.i @@ -45,13 +45,47 @@ static int idaapi py_import_enum_cb( Py_XDECREF(py_result); return r; } + +//------------------------------------------------------------------------- +switch_info_ex_t *switch_info_ex_t_get_clink(PyObject *self) +{ + if ( !PyObject_HasAttrString(self, S_CLINK_NAME) ) + return NULL; + + switch_info_ex_t *r; + PyObject *attr = PyObject_GetAttrString(self, S_CLINK_NAME); + if ( PyCObject_Check(attr) ) + r = (switch_info_ex_t *) PyCObject_AsVoidPtr(attr); + else + r = NULL; + + Py_DECREF(attr); + return r; +} // %} +%rename (get_switch_info_ex) py_get_switch_info_ex; +%rename (set_switch_info_ex) py_set_switch_info_ex; +%rename (del_switch_info_ex) py_del_switch_info_ex; +%rename (create_switch_xrefs) py_create_switch_xrefs; +%rename (create_switch_table) py_create_switch_table; + %inline %{ // + //------------------------------------------------------------------------- -PyObject *py_get_import_module_name(int mod_index) +/* +# +def get_import_module_name(path, fname, callback): + """ + Returns the name of an imported module given its index + @return: None or the module name + """ + pass +# +*/ +static PyObject *py_get_import_module_name(int mod_index) { char buf[MAXSTR]; if ( !get_import_module_name(mod_index, buf, sizeof(buf)) ) @@ -60,14 +94,584 @@ PyObject *py_get_import_module_name(int mod_index) } //------------------------------------------------------------------------- -// enumerate imports from specific module -// return: 1-finished ok, -1 on error, otherwise callback return value (<=0) -int py_enum_import_names(int mod_index, PyObject *py_cb) +/* +# +def get_switch_info_ex(ea): + """ + Returns the a switch_info_ex_t structure containing the information about the switch. + Please refer to the SDK sample 'uiswitch' + @return: None or switch_info_ex_t instance + """ + pass +# +*/ +PyObject *py_get_switch_info_ex(ea_t ea) +{ + switch_info_ex_t *ex = new switch_info_ex_t(); + PyObject *py_obj; + if ( ::get_switch_info_ex(ea, ex, sizeof(switch_info_ex_t)) <= 0 + || (py_obj = create_idaapi_linked_class_instance(S_PY_SWIEX_CLSNAME, ex)) == NULL ) + { + delete ex; + Py_RETURN_NONE; + } + return py_obj; +} + +//------------------------------------------------------------------------- +/* +# +def create_switch_xrefs(insn_ea, si): + """ + This function creates xrefs from the indirect jump. + + Usually there is no need to call this function directly because the kernel + will call it for switch tables + + Note: Custom switch information are not supported yet. + + @param insn_ea: address of the 'indirect jump' instruction + @param si: switch information + + @return: Boolean + """ + pass +# +*/ +idaman bool ida_export py_create_switch_xrefs( + ea_t insn_ea, + PyObject *py_swi) +{ + switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); + if ( swi == NULL ) + return false; + + create_switch_xrefs(insn_ea, swi); + return true; +} + + +//------------------------------------------------------------------------- +/* +# +def create_switch_table(insn_ea, si): + """ + Create switch table from the switch information + + @param insn_ea: address of the 'indirect jump' instruction + @param si: switch information + + @return: Boolean + """ + pass +# +*/ +idaman bool ida_export py_create_switch_table( + ea_t insn_ea, + PyObject *py_swi) +{ + switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); + if ( swi == NULL ) + return false; + + create_switch_table(insn_ea, swi); + return true; +} + +//------------------------------------------------------------------------- +/* +# +def set_switch_info_ex(ea, switch_info_ex): + """ + Saves the switch information in the database + Please refer to the SDK sample 'uiswitch' + @return: Boolean + """ + pass +# +*/ +bool py_set_switch_info_ex(ea_t ea, PyObject *py_swi) +{ + switch_info_ex_t *swi = switch_info_ex_t_get_clink(py_swi); + if ( swi == NULL ) + return false; + + set_switch_info_ex(ea, swi); + return true; +} + +//------------------------------------------------------------------------- +/* +# +def del_switch_info_ex(ea): + """ + Deletes stored switch information + """ + pass +# +*/ +void py_del_switch_info_ex(ea_t ea) +{ + del_switch_info_ex(ea); +} + +//------------------------------------------------------------------------- +/* +# +def enum_import_names(mod_index, callback): + """ + Enumerate imports from a specific module. + Please refer to ex_imports.py example. + + @param mod_index: The module index + @param callback: A callable object that will be invoked with an ea, name (could be None) and ordinal. + @return: 1-finished ok, -1 on error, otherwise callback return value (<=0) + """ + pass +# +*/ +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); } +//------------------------------------------------------------------------- +static PyObject *switch_info_ex_t_create() +{ + switch_info_ex_t *inst = new switch_info_ex_t(); + return PyCObject_FromVoidPtr(inst, NULL); +} + +static bool switch_info_ex_t_destroy(PyObject *py_obj) +{ + if ( !PyCObject_Check(py_obj) ) + return false; + switch_info_ex_t *inst = (switch_info_ex_t *) PyCObject_AsVoidPtr(py_obj); + delete inst; + return true; +} + +static bool switch_info_ex_t_assign(PyObject *self, PyObject *other) +{ + switch_info_ex_t *lhs = switch_info_ex_t_get_clink(self); + switch_info_ex_t *rhs = switch_info_ex_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +// Auto generated - begin +// + +static PyObject *switch_info_ex_t_get_regdtyp(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", (char)link->regdtyp); +} +static void switch_info_ex_t_set_regdtyp(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + link->regdtyp = (char)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_flags2(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("i", link->flags2); +} +static void switch_info_ex_t_set_flags2(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + link->flags2 = (int)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_jcases(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("i", link->jcases); +} +static void switch_info_ex_t_set_jcases(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + link->jcases = (int)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_regnum(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("i", (int)link->regnum); +} +static void switch_info_ex_t_set_regnum(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + link->regnum = (int)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_flags(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", (ushort)link->flags); +} +static void switch_info_ex_t_set_flags(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + link->flags = (uint16)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_ncases(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", (uint16)link->ncases); +} +static void switch_info_ex_t_set_ncases(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + link->ncases = (ushort)PyInt_AsLong(value); +} + +static PyObject *switch_info_ex_t_get_defjump(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->defjump); +} +static void switch_info_ex_t_set_defjump(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); PyGetNumber(value, &v); + link->defjump = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_jumps(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->jumps); +} +static void switch_info_ex_t_set_jumps(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); PyGetNumber(value, &v); + link->jumps = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_elbase(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->elbase); +} +static void switch_info_ex_t_set_elbase(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyGetNumber(value, &v); + link->elbase = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_startea(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->startea); +} +static void switch_info_ex_t_set_startea(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyGetNumber(value, &v); + link->startea = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_custom(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->custom); +} +static void switch_info_ex_t_set_custom(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyGetNumber(value, &v); + link->custom = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_ind_lowcase(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->ind_lowcase); +} +static void switch_info_ex_t_set_ind_lowcase(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyGetNumber(value, &v); + link->ind_lowcase = (pyul_t)v; +} + +static PyObject *switch_info_ex_t_get_values_lowcase(PyObject *self) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->values); +} +static void switch_info_ex_t_set_values_lowcase(PyObject *self, PyObject *value) +{ + switch_info_ex_t *link = switch_info_ex_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyGetNumber(value, &v); + link->values = (pyul_t)v; +} + +// +// Auto generated - end +// +//------------------------------------------------------------------------- // +%} + +%pythoncode %{ +# +SWI_SPARSE = 0x1 +"""sparse switch ( value table present ) otherwise lowcase present""" +SWI_V32 = 0x2 +"""32-bit values in table""" +SWI_J32 = 0x4 +"""32-bit jump offsets""" +SWI_VSPLIT = 0x8 +"""value table is split (only for 32-bit values)""" +SWI_DEFAULT = 0x10 +"""default case is present""" +SWI_END_IN_TBL = 0x20 +"""switchend in table (default entry)""" +SWI_JMP_INV = 0x40 +"""jumptable is inversed (last entry is for first entry in values table)""" +SWI_SHIFT_MASK = 0x180 +"""use formula (element*shift + elbase) to find jump targets""" + +SWI_ELBASE = 0x200 +"""elbase is present (if not and shift!=0, endof(jumpea) is used)""" +SWI_JSIZE = 0x400 +"""jump offset expansion bit""" + +SWI_VSIZE = 0x800 +"""value table element size expansion bit""" + +SWI_SEPARATE = 0x1000 +"""do not create an array of individual dwords""" + +SWI_SIGNED = 0x2000 +"""jump table entries are signed""" + +SWI_CUSTOM = 0x4000 +"""custom jump table - ph.create_switch_xrefs will be called to create code xrefs for the table. it must return 2. custom jump table must be created by the module""" + +SWI_EXTENDED = 0x8000 +"""this is switch_info_ex_t""" + +SWI2_INDIRECT = 0x0001 +"""value table elements are used as indexes into the jump table""" +SWI2_SUBTRACT = 0x0002 +"""table values are subtracted from the elbase instead of being addded""" + +# -------------------------------------------------------------------------- +class switch_info_ex_t(py_clinked_object_t): + def __init__(self, lnk = None): + py_clinked_object_t.__init__(self, lnk) + + def _create_clink(self): + return _idaapi.switch_info_ex_t_create() + + def _del_clink(self, lnk): + return _idaapi.switch_info_ex_t_destroy(lnk) + + def assign(self, other): + return _idaapi.switch_info_ex_t_assign(self, other) + + def is_indirect(self): + return (self.flags & SWI_EXTENDED) != 0 and (self.flags2 & SWI2_INDIRECT) != 0 + + def is_subtract(self): + return (self.flags & SWI_EXTENDED) != 0 and (self.flags2 & SWI2_SUBTRACT) != 0 + + def get_jtable_size(self): + return self.jcases if self.is_indirect() else ncases + + def get_lowcase(self): + return self.ind_lowcase if is_indirect() else self.lowcase + + def set_expr(self, r, dt): + self.regnum = r + self.regdtyp = dt + + def get_shift(self): + return (self.flags & SWI_SHIFT_MASK) >> 7 + + def set_shift(self, shift): + self.flags &= ~SWI_SHIFT_MASK + self.flags |= ((shift & 3) << 7) + + def get_jtable_element_size(self): + code = self.flags & (SWI_J32|SWI_JSIZE) + if code == 0: return 2 + elif code == SWI_J32: return 4 + elif code == SWI_JSIZE: return 1 + else: return 8 + + def set_jtable_element_size(self, size): + self.flags &= ~(SWI_J32|SWI_JSIZE) + if size == 4: self.flags |= SWI_J32 + elif size == 1: self.flags |= SWI_JSIZE + elif size == 8: self.flags |= SWI_J32|SWI_JSIZE + elif size != 2: return False + return True + + def get_vtable_element_size(self): + code = self.flags & (SWI_V32|SWI_VSIZE) + if code == 0: return 2 + elif code == SWI_V32: return 4 + elif code == SWI_VSIZE: return 1 + return 8 + + def set_vtable_element_size(self, size): + self.flags &= ~SWI_V32|SWI_VSIZE + if size == 4: self.flags |= SWI_V32 + elif size == 1: self.flags |= SWI_VSIZE + elif size == 8: self.flags |= SWI_V32|SWI_VSIZE + elif size != 2: return False + return True + + # + # Autogenerated + # + def __get_regdtyp__(self): + return _idaapi.switch_info_ex_t_get_regdtyp(self) + def __set_regdtyp__(self, v): + _idaapi.switch_info_ex_t_set_regdtyp(self, v) + def __get_flags2__(self): + return _idaapi.switch_info_ex_t_get_flags2(self) + def __set_flags2__(self, v): + _idaapi.switch_info_ex_t_set_flags2(self, v) + def __get_jcases__(self): + return _idaapi.switch_info_ex_t_get_jcases(self) + def __set_jcases__(self, v): + _idaapi.switch_info_ex_t_set_jcases(self, v) + def __get_regnum__(self): + return _idaapi.switch_info_ex_t_get_regnum(self) + def __set_regnum__(self, v): + _idaapi.switch_info_ex_t_set_regnum(self, v) + def __get_flags__(self): + return _idaapi.switch_info_ex_t_get_flags(self) + def __set_flags__(self, v): + _idaapi.switch_info_ex_t_set_flags(self, v) + def __get_ncases__(self): + return _idaapi.switch_info_ex_t_get_ncases(self) + def __set_ncases__(self, v): + _idaapi.switch_info_ex_t_set_ncases(self, v) + def __get_defjump__(self): + return _idaapi.switch_info_ex_t_get_defjump(self) + def __set_defjump__(self, v): + _idaapi.switch_info_ex_t_set_defjump(self, v) + def __get_jumps__(self): + return _idaapi.switch_info_ex_t_get_jumps(self) + def __set_jumps__(self, v): + _idaapi.switch_info_ex_t_set_jumps(self, v) + def __get_elbase__(self): + return _idaapi.switch_info_ex_t_get_elbase(self) + def __set_elbase__(self, v): + _idaapi.switch_info_ex_t_set_elbase(self, v) + def __get_startea__(self): + return _idaapi.switch_info_ex_t_get_startea(self) + def __set_startea__(self, v): + _idaapi.switch_info_ex_t_set_startea(self, v) + def __get_custom__(self): + return _idaapi.switch_info_ex_t_get_custom(self) + def __set_custom__(self, v): + _idaapi.switch_info_ex_t_set_custom(self, v) + def __get_ind_lowcase__(self): + return _idaapi.switch_info_ex_t_get_ind_lowcase(self) + def __set_ind_lowcase__(self, v): + _idaapi.switch_info_ex_t_set_ind_lowcase(self, v) + def __get_values_lowcase__(self): + return _idaapi.switch_info_ex_t_get_values_lowcase(self) + def __set_values_lowcase__(self, v): + _idaapi.switch_info_ex_t_set_values_lowcase(self, v) + regdtyp = property(__get_regdtyp__, __set_regdtyp__) + """size of the switch expression register as dtyp""" + flags2 = property(__get_flags2__, __set_flags2__) + jcases = property(__get_jcases__, __set_jcases__) + """number of entries in the jump table (SWI2_INDIRECT)""" + regnum = property(__get_regnum__, __set_regnum__) + """the switch expression as a register number""" + flags = property(__get_flags__, __set_flags__) + """the switch expression as a register number""" + ncases = property(__get_ncases__, __set_ncases__) + """number of cases (excluding default)""" + defjump = property(__get_defjump__, __set_defjump__) + """default jump address""" + jumps = property(__get_jumps__, __set_jumps__) + """jump table address""" + elbase = property(__get_elbase__, __set_elbase__) + """element base""" + startea = property(__get_startea__, __set_startea__) + """start of switch idiom""" + custom = property(__get_custom__, __set_custom__) + """information for custom tables (filled and used by modules)""" + ind_lowcase = property(__get_ind_lowcase__, __set_ind_lowcase__) + values = property(__get_values_lowcase__, __set_values_lowcase__) + lowcase = property(__get_values_lowcase__, __set_values_lowcase__) + +# %} \ No newline at end of file diff --git a/swig/netnode.i b/swig/netnode.i index 1deb080..63725ac 100644 --- a/swig/netnode.i +++ b/swig/netnode.i @@ -39,6 +39,8 @@ %ignore netnode_suplast_idx8; %ignore netnode_supprev_idx8; %ignore netnode_supdel_all; +%ignore netnode_supdel_range; +%ignore netnode_supdel_range_idx8; %ignore netnode_hashval; %ignore netnode_hashstr; %ignore netnode_hashval_long; diff --git a/swig/pro.i b/swig/pro.i index 8a4579b..bd22e02 100644 --- a/swig/pro.i +++ b/swig/pro.i @@ -1,5 +1,57 @@ -%ignore wchar2char; -%ignore hit_counter_t; -%ignore print_all_counters; - -%include "pro.h" +%ignore wchar2char; +%ignore hit_counter_t; +%ignore reg_hit_counter; +%ignore create_hit_counter; +%ignore hit_counter_timer; +%ignore print_all_counters; +%ignore incrementer_t; +%ignore reloc_info_t; // swig under mac chokes on this + +%ignore qstrlen; +%ignore qstrcmp; +%ignore qstrstr; +%ignore qstrchr; +%ignore qstrrchr; +%ignore qstring; +%ignore qvector; +%ignore bytevec_t; +%ignore reloc_info_t; +%ignore relobj_t; +%ignore wchar2char; +%ignore u2cstr; +%ignore c2ustr; +%ignore utf8_unicode; +%ignore win_utf2idb; +%ignore char2oem; +%ignore oem2char; +%ignore set_codepages; +%ignore get_codepages; +%ignore convert_codepage; +%ignore test_bit; +%ignore set_bit; +%ignore clear_bit; +%ignore set_all_bits; +%ignore clear_all_bits; +%ignore interval::overlap; +%ignore interval::includes; +%ignore interval::contains; +%ignore qrotl; +%ignore qrotr; +%ignore setflag; +%ignore read2bytes; +%ignore rotate_left; +%ignore qswap; +%ignore swap32; +%ignore swap16; +%ignore swap_value; +%ignore qalloc_or_throw; +%ignore qrealloc_or_throw; +%ignore launch_process_t; +%ignore init_process; +%ignore term_process; +%ignore get_process_exit_code; +%ignore BELOW_NORMAL_PRIORITY_CLASS; +%ignore parse_command_line; +%rename (parse_command_line) py_parse_command_line; +%include "pro.h" + diff --git a/swig/queue.i b/swig/queue.i index ec30957..8861bdf 100644 --- a/swig/queue.i +++ b/swig/queue.i @@ -10,10 +10,8 @@ %ignore queue_del; %ignore mark_rollback; +%ignore get_rollback_type; %ignore mark_ida_decision; %ignore unmark_ida_decision; -%ignore had_rolled_back; -%ignore ever_rolled_back; - %include "queue.hpp" \ No newline at end of file diff --git a/swig/srarea.i b/swig/srarea.i index 4f62a8d..5dbf875 100644 --- a/swig/srarea.i +++ b/swig/srarea.i @@ -2,6 +2,7 @@ %ignore createSRarea; %ignore killSRareas; %ignore delSRarea; +%ignore splitSRarea; %ignore SRareaStart; %ignore SRareaEnd; %ignore repairSRarea; diff --git a/swig/strlist.i b/swig/strlist.i index 19413df..f58f6c1 100644 --- a/swig/strlist.i +++ b/swig/strlist.i @@ -1,11 +1,11 @@ -// Ignore kernel-only symbol - -%ignore strwinsetup_t::setup_strings_window; -%ignore strwinsetup_t::save_config; -%ignore strwinsetup_t::restore_config; - -%ignore move_strings; - - - -%include "strlist.hpp" +// Ignore kernel-only symbol + +%ignore strwinsetup_t::setup_strings_window; +%ignore strwinsetup_t::save_config; +%ignore strwinsetup_t::restore_config; + +%ignore move_strings; + + + +%include "strlist.hpp" diff --git a/swig/typeinf.i b/swig/typeinf.i index 535c96d..0bd804b 100644 --- a/swig/typeinf.i +++ b/swig/typeinf.i @@ -113,7 +113,10 @@ %ignore get_struct_member; %ignore idb_type_to_til; %ignore get_idb_type; + %ignore apply_type_to_stkarg; +%rename (apply_type_to_stkarg) py_apply_type_to_stkarg; + %ignore use_regarg_type_cb; %ignore set_op_type_t; %ignore is_stkarg_load_t; @@ -144,23 +147,6 @@ %include "typeinf.hpp" -%{ -// -//------------------------------------------------------------------------- -// Utility function to convert a python object to an IDC object -// and sets a python exception on failure. -static bool convert_pyobj_to_idc_exc(PyObject *py_obj, idc_value_t *idc_obj) -{ - int sn = 0; - if (pyvar_to_idcvar(py_obj, idc_obj, &sn) < CIP_OK) - { - PyErr_SetString(PyExc_ValueError, "Could not convert Python object to IDC object!"); - return false; - } - return true; -} -// -%} // Custom wrappers %rename (load_til) load_til_wrap; @@ -177,18 +163,32 @@ PyObject *idc_parse_decl(til_t *ti, const char *decl, int flags) qtype fields, type; qstring name; bool ok = parse_decl(ti, decl, &name, &type, &fields, flags); - if (!ok) + if ( !ok ) Py_RETURN_NONE; - return Py_BuildValue("(sss)", + return Py_BuildValue("(sss)", name.c_str(), (char *)type.c_str(), (char *)fields.c_str()); } //------------------------------------------------------------------------- +/* +# +def get_type_size0(ti, tp): + """ + Returns the size of a type + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @return: + - None on failure + - The size of the type + """ + pass +# +*/ PyObject *py_get_type_size0(const til_t *ti, PyObject *tp) { - if (!PyString_Check(tp)) + if ( !PyString_Check(tp) ) { PyErr_SetString(PyExc_ValueError, "String expected!"); return NULL; @@ -200,13 +200,22 @@ PyObject *py_get_type_size0(const til_t *ti, PyObject *tp) } //------------------------------------------------------------------------- -// Read a typed idc object from the database +/* +# +def py_unpack_object_from_idb(ti, tp, fields, ea, pio_flags = 0): + """ + Unpacks from the database at 'ea' to an object. + Please refer to unpack_object_from_bv() + """ + pass +# +*/ PyObject *py_unpack_object_from_idb( til_t *ti, PyObject *py_type, PyObject *py_fields, ea_t ea, - int pio_flags) + int pio_flags = 0) { if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) { @@ -236,13 +245,30 @@ PyObject *py_unpack_object_from_idb( } //------------------------------------------------------------------------- -// Read a typed idc object from the byte vector +/* +# +def unpack_object_from_bv(ti, tp, fields, bytes, pio_flags = 0): + """ + Unpacks a buffer into an object. + Returns the error_t returned by idaapi.pack_object_to_idb + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @param fields: type fields + @param bytes: the bytes to unpack + @param pio_flags: flags used while unpacking + @return: + - tuple(0, err) on failure + - tuple(1, obj) on success + """ + pass +# +*/ PyObject *py_unpack_object_from_bv( til_t *ti, PyObject *py_type, PyObject *py_fields, PyObject *py_bytes, - int pio_flags) + int pio_flags = 0) { if ( !PyString_Check(py_type) && !PyString_Check(py_fields) && !PyString_Check(py_bytes) ) { @@ -278,18 +304,31 @@ PyObject *py_unpack_object_from_bv( } //------------------------------------------------------------------------- -// Write a typed idc object to the database -// Raises an exception if wrong parameters were passed or conversion fails -// Returns the error_t returned by idasdk.pack_object_to_idb +/* +# +def pack_object_to_idb(obj, ti, tp, fields, ea, pio_flags = 0): + """ + Write a typed object to the database. + Raises an exception if wrong parameters were passed or conversion fails + Returns the error_t returned by idaapi.pack_object_to_idb + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @param fields: type fields + @param ea: ea to be used while packing + @param pio_flags: flags used while unpacking + """ + pass +# +*/ PyObject *py_pack_object_to_idb( PyObject *py_obj, til_t *ti, PyObject *py_type, PyObject *py_fields, ea_t ea, - int pio_flags) + int pio_flags = 0) { - if (!PyString_Check(py_type) && !PyString_Check(py_fields)) + if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) { PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); return NULL; @@ -297,7 +336,7 @@ PyObject *py_pack_object_to_idb( // Convert Python object to IDC object idc_value_t idc_obj; - if (!convert_pyobj_to_idc_exc(py_obj, &idc_obj)) + if ( !convert_pyobj_to_idc_exc(py_obj, &idc_obj) ) return NULL; // Get type strings @@ -310,6 +349,23 @@ PyObject *py_pack_object_to_idb( } //------------------------------------------------------------------------- +/* +# +def pack_object_to_bv(obj, ti, tp, fields, ea, pio_flags = 0): + """ + Packs a typed object to a string + @param ti: Type info. 'idaapi.cvar.idati' can be passed. + @param tp: type string + @param fields: type fields + @param ea: ea to be used while packing + @param pio_flags: flags used while unpacking + @return: + tuple(0, err_code) on failure + tuple(1, packed_buf) on success + """ + pass +# +*/ // Returns a tuple(Boolean, PackedBuffer or Error Code) PyObject *py_pack_object_to_bv( PyObject *py_obj, @@ -319,7 +375,7 @@ PyObject *py_pack_object_to_bv( ea_t base_ea, int pio_flags=0) { - if (!PyString_Check(py_type) && !PyString_Check(py_fields)) + if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) { PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); return NULL; @@ -327,7 +383,7 @@ PyObject *py_pack_object_to_bv( // Convert Python object to IDC object idc_value_t idc_obj; - if (!convert_pyobj_to_idc_exc(py_obj, &idc_obj)) + if ( !convert_pyobj_to_idc_exc(py_obj, &idc_obj) ) return NULL; // Get type strings @@ -337,24 +393,24 @@ PyObject *py_pack_object_to_bv( // Pack relobj_t bytes; error_t err = pack_object_to_bv( - &idc_obj, - ti, - type, - fields, - &bytes, - NULL, + &idc_obj, + ti, + type, + fields, + &bytes, + NULL, pio_flags); - do + do { - if (err != eOk) + if ( err != eOk ) break; - if (!bytes.relocate(base_ea, inf.mf)) + if ( !bytes.relocate(base_ea, inf.mf) ) { err = -1; break; } return Py_BuildValue("(is#)", 1, bytes.begin(), bytes.size()); - } while (false); + } while ( false ); return Py_BuildValue("(ii)", 0, err); } // diff --git a/swig/ua.i b/swig/ua.i index e441a70..510e118 100644 --- a/swig/ua.i +++ b/swig/ua.i @@ -1,40 +1,1351 @@ -// Convert op_t char members to integers -%apply unsigned char { char n }; -%apply unsigned char { char offb }; -%apply unsigned char { char offo }; -%apply unsigned char { char dtyp }; -// Convert insn_t char members to integers -%apply unsigned char { char segpref }; -%apply unsigned char { char insnpref }; -%apply unsigned char { char flags }; +%ignore insn_t; +%ignore op_t; +%ignore cmd; +%ignore ua_out; +%ignore showAsChar; +%ignore out_real; +%ignore init_output_buffer; +%ignore term_output_buffer; +%ignore OutValue; +%ignore OutImmChar; +%ignore out_name_expr; +%ignore ua_stkvar2; +%ignore ua_add_off_drefs; +%ignore ua_add_off_drefs2; +%ignore out_snprintf; +%ignore set_output_ptr; +%ignore get_output_ptr; +%ignore out_insert; +%ignore get_immval; +%ignore get_spoiled_reg; +%ignore construct_macro; +%ignore decode_preceding_insn; +%ignore init_ua; +%ignore term_ua; +%ignore term_uaterm_ua; +%ignore get_equal_items; +%ignore get_equal_itemsget_equal_items; +%ignore ua_use_fixup; + +%ignore get_immval; +%ignore ua_stkvar; %include "ua.hpp" -%clear (char n); -%clear (char offb); -%clear (char offo); -%clear (char dtyp); +%rename (init_output_buffer) py_init_output_buffer; +%rename (term_output_buffer) py_term_output_buffer; +%rename (OutValue) py_OutValue; +%rename (OutImmChar) py_OutImmChar; +%rename (out_name_expr) py_out_name_expr; +%rename (ua_stkvar2) py_ua_stkvar2; +%rename (ua_add_off_drefs) py_ua_add_off_drefs; +%rename (ua_add_off_drefs2) py_ua_add_off_drefs2; +%rename (decode_preceding_insn) py_decode_preceding_insn; -%clear (segpref); -%clear (insnpref); -%clear (flags); +%inline %{ +// -// Small function to get the global cmd pointer -// In Python it returns an insn_t class instance -%inline { -insn_t * get_current_instruction() +//------------------------------------------------------------------------- +/* +# +def init_output_buffer(size = MAXSTR): + """ + This function initialize an output buffer with the given size. + It should be called before using any out_xxxx() functions. + @return: It returns a string. This string should then be passed to MakeLine(). + This function could return None if it failed to create a buffer with the given size. + """ + pass +# +*/ +PyObject *py_init_output_buffer(size_t size = MAXSTR) { - return &cmd; -} + // Let Python allocate a writable string buffer for us + 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; } -// Get the nth operand from the insn_t class -%inline { -op_t *get_instruction_operand(insn_t *ins, int n) +//------------------------------------------------------------------------- +/* +# +def term_output_buffer(): + """Use this function to terminate an output buffer.""" + pass +# +*/ +void py_term_output_buffer() { - if (!ins) - return NULL; - return &(ins->Operands[n]); -} + term_output_buffer(); } +//------------------------------------------------------------------------- +/* +# +def decode_preceding_insn(ea): + """ + Decodes the preceding instruction. Please check ua.hpp / decode_preceding_insn() + @param ea: current ea + @return: tuple(preceeding_ea or BADADDR, farref = Boolean) + """ + pass +# +*/ +PyObject *py_decode_preceding_insn(ea_t ea) +{ + bool farref; + ea_t r = decode_preceding_insn(ea, &farref); + return Py_BuildValue("(" PY_FMT64 "i)", pyul_t(r), farref ? 1 : 0); +} + +//------------------------------------------------------------------------- +/* +# +def OutValue(op, outflags = 0): + """ + Output immediate value + @param op: operand (of type op_t) + @return: flags of the output value + -1: value is output with COLOR_ERROR + 0: value is output as a number or character or segment + """ + pass +# +*/ +flags_t py_OutValue(PyObject *x, int outflags=0) +{ + op_t *op = op_t_get_clink(x); + if ( op == NULL ) + return 0; + + return OutValue(*op, outflags); +} + +//------------------------------------------------------------------------- +/* +# +def get_stkvar(op, v): + """ + Get pointer to stack variable + @param op: reference to instruction operand + @param v: immediate value in the operand (usually op.addr) + @return: + - None on failure + - tuple(member_t, actval) + where actval: actual value used to fetch stack variable + """ + pass +# +*/ +PyObject *py_get_stkvar(PyObject *py_op, PyObject *py_v) +{ + op_t *op = op_t_get_clink(py_op); + uint64 v; + if ( op == NULL || !PyGetNumber(py_v, &v) ) + Py_RETURN_NONE; + + sval_t actval; + member_t *member = get_stkvar(*op, sval_t(v), &actval); + if ( member == NULL ) + Py_RETURN_NONE; + + return Py_BuildValue("(O" PY_SFMT64 ")", + SWIG_NewPointerObj(SWIG_as_voidptr(member), SWIGTYPE_p_member_t, 0), + pyl_t(actval)); +} + +//------------------------------------------------------------------------- +/* +header: frame.hpp +# +def add_stkvar3(op, v, flags): + """ + Automatically add stack variable if doesn't exist + Processor modules should use ua_stkvar2() + @param op: reference to instruction operand + @param v: immediate value in the operand (usually op.addr) + @param flags: combination of STKVAR_... constants + @return: Boolean + """ + pass +# +*/ +bool py_add_stkvar3(PyObject *py_op, PyObject *py_v, int flags) +{ + op_t *op = op_t_get_clink(py_op); + uint64 v; + return ( op == NULL || !PyGetNumber(py_v, &v) || !add_stkvar3(*op, sval_t(v), flags)) ? false : true; +} + +//------------------------------------------------------------------------- +/* +header: frame.hpp +// Calculate offset of stack variable in the frame structure +// pfn - pointer to function (can't be NULL!) +// x - reference to instruction operand +// v - value of variable offset in the instruction +// returns: offset of stack variable in the frame structure (0..n) + +ea_t calc_frame_offset(func_t *pfn, const op_t *x, sval_t v); +*/ + +//------------------------------------------------------------------------- +/* +header: typeinf.hpp +# +def apply_type_to_stkarg(op, v, type, name): + """ + Apply type information to a stack variable + + @param op: reference to instruction operand + @param v: immediate value in the operand (usually op.addr) + @param type: type string. Retrieve from idc.ParseType("type string", flags)[1] + @param name: stack variable name + + @return: Boolean + """ + pass +# +*/ +bool py_apply_type_to_stkarg( + PyObject *py_op, + PyObject *py_uv, + PyObject *py_type, + const char *name) +{ + uint64 v; + op_t *op = op_t_get_clink(py_op); + if ( op == NULL || !PyGetNumber(py_uv, &v) || !PyString_Check(py_type)) + return false; + else + return apply_type_to_stkarg(*op, uval_t(v), (type_t *) PyString_AsString(py_type), name); +} + +//------------------------------------------------------------------------- +/* +# +def OutImmChar(op, outflags = 0): + """ + Output operand value as a commented character constant + @param op: operand (of type op_t) + @return: None + """ + pass +# +*/ +static void py_OutImmChar(PyObject *x) +{ + op_t *op = op_t_get_clink(x); + if ( op != NULL ) + OutImmChar(*op); +} + +//------------------------------------------------------------------------- +/* +# +def ua_stkvar2(op, outflags = 0): + """ + Create or modify a stack variable in the function frame. + Please check ua.hpp / ua_stkvar2() + @param op: operand (of type op_t) + @return: None + """ + pass +# +*/ +static bool py_ua_stkvar2(PyObject *x, adiff_t v, int flags) +{ + op_t *op = op_t_get_clink(x); + return op == NULL ? false : ua_stkvar2(*op, v, flags); +} + +//------------------------------------------------------------------------- +/* +# +def ua_add_off_drefs(op, type): + """ + Add xrefs for offset operand of the current instruction + Please check ua.hpp / ua_add_off_drefs() + @param op: operand (of type op_t) + @return: None + """ + pass +# +*/ +ea_t py_ua_add_off_drefs(PyObject *py_op, dref_t type) +{ + op_t *op = op_t_get_clink(py_op); + return op == NULL ? BADADDR : ua_add_off_drefs(*op, type); +} + +//------------------------------------------------------------------------- +/* +# +def ua_add_off_drefs2(op, type, outf): + """ + Add xrefs for offset operand of the current instruction + Please check ua.hpp / ua_add_off_drefs2() + @return: ea_t + """ + pass +# +*/ +ea_t py_ua_add_off_drefs2(PyObject *py_op, dref_t type, int outf) +{ + op_t *op = op_t_get_clink(py_op); + return op == NULL ? BADADDR : ua_add_off_drefs2(*op, type, outf); +} + +//------------------------------------------------------------------------- +/* +# +def out_name_expr(op, ea, off): + """ + Output a name expression + @param op: operand (of type op_t) + @param ea: address of expression + @param off: the value of name expression. this parameter is used only to + check that the name expression will have the wanted value. + You may pass BADADDR for this parameter. + @return: true if the name expression has been produced + """ + pass +# +*/ +bool py_out_name_expr( + PyObject *py_op, + ea_t ea, + PyObject *py_off) +{ + op_t *op = op_t_get_clink(py_op); + uint64 v(0); + adiff_t off; + if ( PyGetNumber(py_off, &v) ) + off = adiff_t(v); + else + off = BADADDR; + return op == NULL ? false : out_name_expr(*op, ea, off); +} + +//------------------------------------------------------------------------- +static bool op_t_assign(PyObject *self, PyObject *other) +{ + op_t *lhs = op_t_get_clink(self); + op_t *rhs = op_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +static bool insn_t_assign(PyObject *self, PyObject *other) +{ + insn_t *lhs = insn_t_get_clink(self); + insn_t *rhs = insn_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_get_op_link(PyObject *py_insn_lnk, int i) +{ + if ( i < 0 || i >= UA_MAXOP || !PyCObject_Check(py_insn_lnk) ) + Py_RETURN_NONE; + + // Extract C link + insn_t *insn = (insn_t *)PyCObject_AsVoidPtr(py_insn_lnk); + + // Return a link to the operand + return PyCObject_FromVoidPtr(&insn->Operands[i], NULL); +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_create() +{ + insn_t *insn = new insn_t(); + return PyCObject_FromVoidPtr(insn, NULL); +} + +//------------------------------------------------------------------------- +static PyObject *op_t_create() +{ + op_t *op = new op_t(); + return PyCObject_FromVoidPtr(op, NULL); +} + +//------------------------------------------------------------------------- +static bool op_t_destroy(PyObject *py_obj) +{ + if ( !PyCObject_Check(py_obj) ) + return false; + + op_t *op = (op_t *) PyCObject_AsVoidPtr(py_obj); + delete op; + + return true; +} + +//------------------------------------------------------------------------- +static bool insn_t_destroy(PyObject *py_obj) +{ + if ( !PyCObject_Check(py_obj) ) + return false; + + insn_t *insn = (insn_t *) PyCObject_AsVoidPtr(py_obj); + delete insn; + + return true; +} + +//------------------------------------------------------------------------- +// Returns a C link to the global 'cmd' variable +static PyObject *py_get_global_cmd_link() +{ + return PyCObject_FromVoidPtr(&::cmd, NULL); +} + +//------------------------------------------------------------------------- +PyObject *insn_t_is_canon_insn(int itype) +{ + if ( ph.is_canon_insn(itype) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +PyObject *insn_t_get_canon_feature(int itype) +{ + return Py_BuildValue("I", ph.is_canon_insn(itype) ? ph.instruc[itype-ph.instruc_start].feature : 0); +} + +//------------------------------------------------------------------------- +PyObject *insn_t_get_canon_mnem(int itype) +{ + if ( ph.is_canon_insn(itype) ) + return Py_BuildValue("s", ph.instruc[itype-ph.instruc_start].name); + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_get_cs(PyObject *self) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->cs); +} + +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); + PyGetNumber(value, &v); + link->cs = ea_t(v); +} + +static PyObject *insn_t_get_ip(PyObject *self) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->ip); +} + +static void insn_t_set_ip(PyObject *self, PyObject *value) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyGetNumber(value, &v); + link->ip = ea_t(v); +} + +static PyObject *insn_t_get_ea(PyObject *self) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->ea); +} + +static void insn_t_set_ea(PyObject *self, PyObject *value) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyGetNumber(value, &v); + link->ea = ea_t(v); +} + +static PyObject *insn_t_get_itype(PyObject *self) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->itype); +} + +static void insn_t_set_itype(PyObject *self, PyObject *value) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->itype = (uint16)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_size(PyObject *self) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->size); +} + +static void insn_t_set_size(PyObject *self, PyObject *value) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->size = (uint16)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_auxpref(PyObject *self) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->auxpref); +} + +static void insn_t_set_auxpref(PyObject *self, PyObject *value) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->auxpref = (uint16)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_segpref(PyObject *self) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->segpref); +} + +static void insn_t_set_segpref(PyObject *self, PyObject *value) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->segpref = (char)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_insnpref(PyObject *self) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->insnpref); +} + +static void insn_t_set_insnpref(PyObject *self, PyObject *value) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->insnpref = (char)PyInt_AsLong(value); +} + +static PyObject *insn_t_get_flags(PyObject *self) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->flags); +} + +static void insn_t_set_flags(PyObject *self, PyObject *value) +{ + insn_t *link = insn_t_get_clink(self); + if ( link == NULL ) + return; + link->flags = (char)PyInt_AsLong(value); +} + +//------------------------------------------------------------------------- +static PyObject *op_t_get_n(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->n); +} + +static void op_t_set_n(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->n = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_type(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("B", link->type); +} + +static void op_t_set_type(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->type = (optype_t)PyInt_AsLong(value); +} + +static PyObject *op_t_get_offb(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->offb); +} + +static void op_t_set_offb(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->offb = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_offo(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->offo); +} + +static void op_t_set_offo(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->offo = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_flags(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("B", link->flags); +} + +static void op_t_set_flags(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->flags = (uchar)PyInt_AsLong(value); +} + +static PyObject *op_t_get_dtyp(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->dtyp); +} + +static void op_t_set_dtyp(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->dtyp = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_reg_phrase(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("H", link->reg); +} +static void op_t_set_reg_phrase(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->reg = (uint16)PyInt_AsLong(value); +} + +static PyObject *op_t_get_value(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("I", link->value); +} + +static void op_t_set_value(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->value = PyInt_AsLong(value); +} + +static PyObject *op_t_get_addr(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->addr); +} + +static void op_t_set_addr(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyGetNumber(value, &v); + link->addr = ea_t(v); +} + +static PyObject *op_t_get_specval(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(PY_FMT64, (pyul_t)link->specval); +} + +static void op_t_set_specval(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + uint64 v(0); + PyGetNumber(value, &v); + link->specval = ea_t(v); +} + +static PyObject *op_t_get_specflag1(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag1); +} + +static void op_t_set_specflag1(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag1 = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_specflag2(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag2); +} + +static void op_t_set_specflag2(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag2 = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_specflag3(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag3); +} + +static void op_t_set_specflag3(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag3 = (char)PyInt_AsLong(value); +} + +static PyObject *op_t_get_specflag4(PyObject *self) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("b", link->specflag4); +} + +static void op_t_set_specflag4(PyObject *self, PyObject *value) +{ + op_t *link = op_t_get_clink(self); + if ( link == NULL ) + return; + link->specflag4 = (char)PyInt_AsLong(value); +} + +// +%} + +%{ +// +//------------------------------------------------------------------------- +insn_t *insn_t_get_clink(PyObject *self) +{ + // Must have the link attribute + if ( !PyObject_HasAttrString(self, S_CLINK_NAME) ) + return NULL; + + insn_t *insn; + PyObject *attr = PyObject_GetAttrString(self, S_CLINK_NAME); + if ( PyCObject_Check(attr) ) + insn = (insn_t *) PyCObject_AsVoidPtr(attr); + else + insn = NULL; + Py_DECREF(attr); + return insn; +} + +//------------------------------------------------------------------------- +op_t *op_t_get_clink(PyObject *self) +{ + // Must have the link attribute + if ( !PyObject_HasAttrString(self, S_CLINK_NAME) ) + return NULL; + op_t *r; + PyObject *attr = PyObject_GetAttrString(self, S_CLINK_NAME); + if ( PyCObject_Check(attr) ) + r = (op_t *) PyCObject_AsVoidPtr(attr); + else + r = NULL; + Py_DECREF(attr); + return r; +} + +// +%} + +%pythoncode %{ +# + +# ----------------------------------------------------------------------- +class op_t(py_clinked_object_t): + """Class representing operands""" + def __init__(self, lnk = None): + py_clinked_object_t.__init__(self, lnk) + + def _create_clink(self): + return _idaapi.op_t_create() + + def _del_clink(self, lnk): + return _idaapi.op_t_destroy(lnk) + + def assign(self, other): + """Copies the contents of 'other' to 'self'""" + return _idaapi.op_t_assign(self, other) + +# +# def copy(self): +# """Returns a new copy of this class""" +# pass +# + + def __eq__(self, other): + """Checks if two register operands are equal by checking the register number and its dtype""" + return (self.reg == other.reg) and (self.dtyp == other.dtyp) + + def is_reg(self, r): + """Checks if the register operand is the given processor register""" + return self.type == idaapi.o_reg and self == r + + def has_reg(self, r): + """Checks if the operand accesses the given processor register""" + return self.reg == r.reg + + # + # Autogenerated + # + def __get_n__(self): + return _idaapi.op_t_get_n(self) + def __set_n__(self, v): + _idaapi.op_t_set_n(self, v) + def __get_type__(self): + return _idaapi.op_t_get_type(self) + def __set_type__(self, v): + _idaapi.op_t_set_type(self, v) + def __get_offb__(self): + return _idaapi.op_t_get_offb(self) + def __set_offb__(self, v): + _idaapi.op_t_set_offb(self, v) + def __get_offo__(self): + return _idaapi.op_t_get_offo(self) + def __set_offo__(self, v): + _idaapi.op_t_set_offo(self, v) + def __get_flags__(self): + return _idaapi.op_t_get_flags(self) + def __set_flags__(self, v): + _idaapi.op_t_set_flags(self, v) + def __get_dtyp__(self): + return _idaapi.op_t_get_dtyp(self) + def __set_dtyp__(self, v): + _idaapi.op_t_set_dtyp(self, v) + def __get_reg_phrase__(self): + return _idaapi.op_t_get_reg_phrase(self) + def __set_reg_phrase__(self, v): + _idaapi.op_t_set_reg_phrase(self, v) + def __get_value__(self): + return _idaapi.op_t_get_value(self) + def __set_value__(self, v): + _idaapi.op_t_set_value(self, v) + def __get_addr__(self): + return _idaapi.op_t_get_addr(self) + def __set_addr__(self, v): + _idaapi.op_t_set_addr(self, v) + def __get_specval__(self): + return _idaapi.op_t_get_specval(self) + def __set_specval__(self, v): + _idaapi.op_t_set_specval(self, v) + def __get_specflag1__(self): + return _idaapi.op_t_get_specflag1(self) + def __set_specflag1__(self, v): + _idaapi.op_t_set_specflag1(self, v) + def __get_specflag2__(self): + return _idaapi.op_t_get_specflag2(self) + def __set_specflag2__(self, v): + _idaapi.op_t_set_specflag2(self, v) + def __get_specflag3__(self): + return _idaapi.op_t_get_specflag3(self) + def __set_specflag3__(self, v): + _idaapi.op_t_set_specflag3(self, v) + def __get_specflag4__(self): + return _idaapi.op_t_get_specflag4(self) + def __set_specflag4__(self, v): + _idaapi.op_t_set_specflag4(self, v) + + n = property(__get_n__, __set_n__) + type = property(__get_type__, __set_type__) + offb = property(__get_offb__, __set_offb__) + offo = property(__get_offo__, __set_offo__) + flags = property(__get_flags__, __set_flags__) + dtyp = property(__get_dtyp__, __set_dtyp__) + reg = property(__get_reg_phrase__, __set_reg_phrase__) + phrase = property(__get_reg_phrase__, __set_reg_phrase__) + value = property(__get_value__, __set_value__) + addr = property(__get_addr__, __set_addr__) + specval = property(__get_specval__, __set_specval__) + specflag1 = property(__get_specflag1__, __set_specflag1__) + specflag2 = property(__get_specflag2__, __set_specflag2__) + specflag3 = property(__get_specflag3__, __set_specflag3__) + specflag4 = property(__get_specflag4__, __set_specflag4__) + +# ----------------------------------------------------------------------- +class insn_t(py_clinked_object_t): + """Class representing instructions""" + def __init__(self, lnk = None): + py_clinked_object_t.__init__(self, lnk) + + # Create linked operands + self.Operands = [] + for i in xrange(0, UA_MAXOP): + self.Operands.append(op_t(insn_t_get_op_link(self.clink, i))) + + # Convenience operand reference objects + self.Op1 = self.Operands[0] + self.Op2 = self.Operands[1] + self.Op3 = self.Operands[2] + self.Op4 = self.Operands[3] + self.Op5 = self.Operands[4] + self.Op6 = self.Operands[5] + + def assign(self, other): + """Copies the contents of 'other' to 'self'""" + return _idaapi.insn_t_assign(self, other) + +# +# def copy(self): +# """Returns a new copy of this class""" +# pass +# + + def _create_clink(self): + return _idaapi.insn_t_create() + + def _del_clink(self, lnk): + return _idaapi.insn_t_destroy(lnk) + + 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] + + def is_macro(self): + return self.flags & INSN_MACRO != 0 + + def is_canon_insn(self): + return _idaapi.insn_t_is_canon_insn(self.itype) + + def get_canon_feature(self): + return _idaapi.insn_t_get_canon_feature(self.itype) + + def get_canon_mnem(self): + return _idaapi.insn_t_get_canon_mnem(self.itype) + + # + # Autogenerated + # + def __get_cs__(self): + return _idaapi.insn_t_get_cs(self) + def __set_cs__(self, v): + _idaapi.insn_t_set_cs(self, v) + def __get_ip__(self): + return _idaapi.insn_t_get_ip(self) + def __set_ip__(self, v): + _idaapi.insn_t_set_ip(self, v) + def __get_ea__(self): + return _idaapi.insn_t_get_ea(self) + def __set_ea__(self, v): + _idaapi.insn_t_set_ea(self, v) + def __get_itype__(self): + return _idaapi.insn_t_get_itype(self) + def __set_itype__(self, v): + _idaapi.insn_t_set_itype(self, v) + def __get_size__(self): + return _idaapi.insn_t_get_size(self) + def __set_size__(self, v): + _idaapi.insn_t_set_size(self, v) + def __get_auxpref__(self): + return _idaapi.insn_t_get_auxpref(self) + def __set_auxpref__(self, v): + _idaapi.insn_t_set_auxpref(self, v) + def __get_segpref__(self): + return _idaapi.insn_t_get_segpref(self) + def __set_segpref__(self, v): + _idaapi.insn_t_set_segpref(self, v) + def __get_insnpref__(self): + return _idaapi.insn_t_get_insnpref(self) + def __set_insnpref__(self, v): + _idaapi.insn_t_set_insnpref(self, v) + def __get_flags__(self): + return _idaapi.insn_t_get_flags(self) + def __set_flags__(self, v): + _idaapi.insn_t_set_flags(self, v) + + cs = property(__get_cs__, __set_cs__) + ip = property(__get_ip__, __set_ip__) + ea = property(__get_ea__, __set_ea__) + itype = property(__get_itype__, __set_itype__) + size = property(__get_size__, __set_size__) + auxpref = property(__get_auxpref__, __set_auxpref__) + segpref = property(__get_segpref__, __set_segpref__) + insnpref = property(__get_insnpref__, __set_insnpref__) + flags = property(__get_flags__, __set_flags__) + + +#---------------------------------------------------------------------------- +# P R O C E S S O R M O D U L E S C O N S T A N T S +#---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------- +# processor_t related constants + +CUSTOM_CMD_ITYPE = 0x8000 +REG_SPOIL = 0x80000000 + +REAL_ERROR_FORMAT = -1 # not supported format for current .idp +REAL_ERROR_RANGE = -2 # number too big (small) for store (mem NOT modifyed) +REAL_ERROR_BADDATA = -3 # illegal real data for load (IEEE data not filled) + +# +# Check whether the operand is relative to stack pointer or frame pointer. +# This function is used to determine how to output a stack variable +# This function may be absent. If it is absent, then all operands +# are sp based by default. +# Define this function only if some stack references use frame pointer +# instead of stack pointer. +# returns flags: +OP_FP_BASED = 0x00000000 # operand is FP based +OP_SP_BASED = 0x00000001 # operand is SP based +OP_SP_ADD = 0x00000000 # operand value is added to the pointer +OP_SP_SUB = 0x00000002 # operand value is substracted from the pointer + +# processor_t.id +PLFM_386 = 0x0 # Intel 80x86 +PLFM_Z80 = 0x1 # 8085, Z80 +PLFM_I860 = 0x2 # Intel 860 +PLFM_8051 = 0x3 # 8051 +PLFM_TMS = 0x4 # Texas Instruments TMS320C5x +PLFM_6502 = 0x5 # 6502 +PLFM_PDP = 0x6 # PDP11 +PLFM_68K = 0x7 # Motoroal 680x0 +PLFM_JAVA = 0x8 # Java +PLFM_6800 = 0x9 # Motorola 68xx +PLFM_ST7 = 0x10 # SGS-Thomson ST7 +PLFM_MC6812 = 0x11 # Motorola 68HC12 +PLFM_MIPS = 0x12 # MIPS +PLFM_ARM = 0x13 # Advanced RISC Machines +PLFM_TMSC6 = 0x14 # Texas Instruments TMS320C6x +PLFM_PPC = 0x15 # PowerPC +PLFM_80196 = 0x16 # Intel 80196 +PLFM_Z8 = 0x17 # Z8 +PLFM_SH = 0x18 # Renesas (formerly Hitachi) SuperH +PLFM_NET = 0x19 # Microsoft Visual Studio.Net +PLFM_AVR = 0x20 # Atmel 8-bit RISC processor(s) +PLFM_H8 = 0x21 # Hitachi H8/300, H8/2000 +PLFM_PIC = 0x22 # Microchip's PIC +PLFM_SPARC = 0x23 # SPARC +PLFM_ALPHA = 0x24 # DEC Alpha +PLFM_HPPA = 0x25 # Hewlett-Packard PA-RISC +PLFM_H8500 = 0x26 # Hitachi H8/500 +PLFM_TRICORE = 0x27 # Tasking Tricore +PLFM_DSP56K = 0x28 # Motorola DSP5600x +PLFM_C166 = 0x29 # Siemens C166 family +PLFM_ST20 = 0x30 # SGS-Thomson ST20 +PLFM_IA64 = 0x31 # Intel Itanium IA64 +PLFM_I960 = 0x32 # Intel 960 +PLFM_F2MC = 0x33 # Fujistu F2MC-16 +PLFM_TMS320C54 = 0x34 # Texas Instruments TMS320C54xx +PLFM_TMS320C55 = 0x35 # Texas Instruments TMS320C55xx +PLFM_TRIMEDIA = 0x36 # Trimedia +PLFM_M32R = 0x37 # Mitsubishi 32bit RISC +PLFM_NEC_78K0 = 0x38 # NEC 78K0 +PLFM_NEC_78K0S = 0x39 # NEC 78K0S +PLFM_M740 = 0x40 # Mitsubishi 8bit +PLFM_M7700 = 0x41 # Mitsubishi 16bit +PLFM_ST9 = 0x42 # ST9+ +PLFM_FR = 0x43 # Fujitsu FR Family +PLFM_MC6816 = 0x44 # Motorola 68HC16 +PLFM_M7900 = 0x45 # Mitsubishi 7900 +PLFM_TMS320C3 = 0x46 # Texas Instruments TMS320C3 +PLFM_KR1878 = 0x47 # Angstrem KR1878 +PLFM_AD218X = 0x48 # Analog Devices ADSP 218X +PLFM_OAKDSP = 0x49 # Atmel OAK DSP +PLFM_TLCS900 = 0x50 # Toshiba TLCS-900 +PLFM_C39 = 0x51 # Rockwell C39 +PLFM_CR16 = 0x52 # NSC CR16 +PLFM_MN102L00 = 0x53 # Panasonic MN10200 +PLFM_TMS320C1X = 0x54 # Texas Instruments TMS320C1x +PLFM_NEC_V850X = 0x55 # NEC V850 and V850ES/E1/E2 +PLFM_SCR_ADPT = 0x56 # Processor module adapter for processor modules written in scripting languages +PLFM_EBC = 0x57 # EFI Bytecode +PLFM_MSP430 = 0x58 # Texas Instruments MSP430 + +# +# processor_t.flag +# +PR_SEGS = 0x000001 # has segment registers? +PR_USE32 = 0x000002 # supports 32-bit addressing? +PR_DEFSEG32 = 0x000004 # segments are 32-bit by default +PR_RNAMESOK = 0x000008 # allow to user register names for location names +PR_ADJSEGS = 0x000020 # IDA may adjust segments moving their starting/ending addresses. +PR_DEFNUM = 0x0000C0 # default number representation: +PRN_HEX = 0x000000 # hex +PRN_OCT = 0x000040 # octal +PRN_DEC = 0x000080 # decimal +PRN_BIN = 0x0000C0 # binary +PR_WORD_INS = 0x000100 # instruction codes are grouped 2bytes in binrary line prefix +PR_NOCHANGE = 0x000200 # The user can't change segments and code/data attributes (display only) +PR_ASSEMBLE = 0x000400 # Module has a built-in assembler and understands IDP_ASSEMBLE +PR_ALIGN = 0x000800 # All data items should be aligned properly +PR_TYPEINFO = 0x001000 # the processor module supports + # type information callbacks + # ALL OF THEM SHOULD BE IMPLEMENTED! + # (the ones >= decorate_name) +PR_USE64 = 0x002000 # supports 64-bit addressing? +PR_SGROTHER = 0x004000 # the segment registers don't contain + # the segment selectors, something else +PR_STACK_UP = 0x008000 # the stack grows up +PR_BINMEM = 0x010000 # the processor module provides correct + # segmentation for binary files + # (i.e. it creates additional segments) + # The kernel will not ask the user + # to specify the RAM/ROM sizes +PR_SEGTRANS = 0x020000 # the processor module supports + # the segment translation feature + # (it means it calculates the code + # addresses using the codeSeg() function) +PR_CHK_XREF = 0x040000 # don't allow near xrefs between segments + # with different bases +PR_NO_SEGMOVE = 0x080000 # the processor module doesn't support move_segm() + # (i.e. the user can't move segments) +PR_FULL_HIFXP = 0x100000 # REF_VHIGH operand value contains full operand + # not only the high bits. Meaningful if ph.high_fixup_bits +PR_USE_ARG_TYPES = 0x200000 # use ph.use_arg_types callback +PR_SCALE_STKVARS = 0x400000 # use ph.get_stkvar_scale callback +PR_DELAYED = 0x800000 # has delayed jumps and calls +PR_ALIGN_INSN = 0x1000000 # allow ida to create alignment instructions + # arbirtrarily. Since these instructions + # might lead to other wrong instructions + # and spoil the listing, IDA does not create + # them by default anymore +PR_PURGING = 0x2000000 # there are calling conventions which may + # purge bytes from the stack +PR_CNDINSNS = 0x4000000 # has conditional instructions +PR_USE_TBYTE = 0x8000000 # BTMT_SPECFLT means _TBYTE type +PR_DEFSEG64 = 0x10000000 # segments are 64-bit by default + + +# ---------------------------------------------------------------------- +# +# Misc constants +# +UA_MAXOP = 6 +"""The maximum number of operands in the insn_t structure""" + +# Create 'cmd' into the global scope +cmd = insn_t(_idaapi.py_get_global_cmd_link()) +"""cmd is a global variable of type insn_t. It is contains information about the last decoded instruction. +This variable is also filled by processor modules when they decode instructions.""" + +# ---------------------------------------------------------------------- +# instruc_t related constants + +# +# instruc_t.feature +# +CF_STOP = 0x00001 # Instruction doesn't pass execution to the next instruction +CF_CALL = 0x00002 # CALL instruction (should make a procedure here) +CF_CHG1 = 0x00004 # The instruction modifies the first operand +CF_CHG2 = 0x00008 # The instruction modifies the second operand +CF_CHG3 = 0x00010 # The instruction modifies the third operand +CF_CHG4 = 0x00020 # The instruction modifies 4 operand +CF_CHG5 = 0x00040 # The instruction modifies 5 operand +CF_CHG6 = 0x00080 # The instruction modifies 6 operand +CF_USE1 = 0x00100 # The instruction uses value of the first operand +CF_USE2 = 0x00200 # The instruction uses value of the second operand +CF_USE3 = 0x00400 # The instruction uses value of the third operand +CF_USE4 = 0x00800 # The instruction uses value of the 4 operand +CF_USE5 = 0x01000 # The instruction uses value of the 5 operand +CF_USE6 = 0x02000 # The instruction uses value of the 6 operand +CF_JUMP = 0x04000 # The instruction passes execution using indirect jump or call (thus needs additional analysis) +CF_SHFT = 0x08000 # Bit-shift instruction (shl,shr...) +CF_HLL = 0x10000 # Instruction may be present in a high level language function. + +# ---------------------------------------------------------------------- +# op_t related constants + +# +# op_t.type +# Description Data field +o_void = 0 # No Operand ---------- +o_reg = 1 # General Register (al,ax,es,ds...) reg +o_mem = 2 # Direct Memory Reference (DATA) addr +o_phrase = 3 # Memory Ref [Base Reg + Index Reg] phrase +o_displ = 4 # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr +o_imm = 5 # Immediate Value value +o_far = 6 # Immediate Far Address (CODE) addr +o_near = 7 # Immediate Near Address (CODE) addr +o_idpspec0 = 8 # IDP specific type +o_idpspec1 = 9 # IDP specific type +o_idpspec2 = 10 # IDP specific type +o_idpspec3 = 11 # IDP specific type +o_idpspec4 = 12 # IDP specific type +o_idpspec5 = 13 # IDP specific type +o_last = 14 # first unused type + +# +# op_t.dtyp +# +dt_byte = 0 # 8 bit +dt_word = 1 # 16 bit +dt_dword = 2 # 32 bit +dt_float = 3 # 4 byte +dt_double = 4 # 8 byte +dt_tbyte = 5 # variable size (ph.tbyte_size) +dt_packreal = 6 # packed real format for mc68040 +dt_qword = 7 # 64 bit +dt_byte16 = 8 # 128 bit +dt_code = 9 # ptr to code (not used?) +dt_void = 10 # none +dt_fword = 11 # 48 bit +dt_bitfild = 12 # bit field (mc680x0) +dt_string = 13 # pointer to asciiz string +dt_unicode = 14 # pointer to unicode string +dt_3byte = 15 # 3-byte data +dt_ldbl = 16 # long double (which may be different from tbyte) + +# +# op_t.flags +# +OF_NO_BASE_DISP = 0x80 # o_displ: base displacement doesn't exist meaningful only for o_displ type if set, base displacement (x.addr) doesn't exist. +OF_OUTER_DISP = 0x40 # o_displ: outer displacement exists meaningful only for o_displ type if set, outer displacement (x.value) exists. +PACK_FORM_DEF = 0x20 # !o_reg + dt_packreal: packed factor defined +OF_NUMBER = 0x10 # can be output as number only if set, the operand can be converted to a number only +OF_SHOW = 0x08 # should the operand be displayed? if clear, the operand is hidden and should not be displayed + +# +# insn_t.flags +# +INSN_MACRO = 0x01 # macro instruction +INSN_MODMAC = 0x02 # macros: may modify the database to make room for the macro insn + +# +# Set IDP options constants +# +IDPOPT_STR = 1 # string constant +IDPOPT_NUM = 2 # number +IDPOPT_BIT = 3 # bit, yes/no +IDPOPT_FLT = 4 # float +IDPOPT_I64 = 5 # 64bit number + +IDPOPT_OK = 0 # ok +IDPOPT_BADKEY = 1 # illegal keyword +IDPOPT_BADTYPE = 2 # illegal type of value +IDPOPT_BADVALUE = 3 # illegal value (bad range, for example) + +# ---------------------------------------------------------------------- +class processor_t(pyidc_opaque_object_t): + """Base class for all processor module scripts""" + def __init__(self): + # Take a reference to 'cmd' + self.cmd = cmd + + def get_idpdesc(self): + """ + This function must be present and should return the list of + short processor names similar to the one in ph.psnames. + This method can be overridden to return to the kernel a different IDP description. + """ + return self.plnames[0] + ':' + ':'.join(self.psnames) + + def get_uFlag(self): + """Use this utility function to retrieve the 'uFlag' global variable""" + return _idaapi.cvar.uFlag + + def get_auxpref(self): + """This function returns cmd.auxpref value""" + return self.cmd.auxpref + +# +%} diff --git a/swig/xref.i b/swig/xref.i index e632091..289a34b 100644 --- a/swig/xref.i +++ b/swig/xref.i @@ -9,6 +9,8 @@ %ignore has_jump_or_flow_xref; %ignore has_call_xref; %ignore destroy_switch_info; +%ignore create_switch_xrefs; +%ignore create_switch_table; // These functions should not be called directly (according to docs) %ignore xrefblk_t_first_from; diff --git a/tools/swigdocs.py b/tools/swigdocs.py new file mode 100644 index 0000000..a9b85d4 --- /dev/null +++ b/tools/swigdocs.py @@ -0,0 +1,85 @@ +# ----------------------------------------------------------------------- +# This script is used to extract embedded documentation strings +# from SWIG interface files. +# (c) Hex-Rays +# +import glob +import sys + +# --------------------------------------------------------------------------- +def extract_docs(lines, out): + S_SWIG_CLOSE = '%}' + S_PYDOC_START = '#' + S_PYDOC_END = '#' + S_COMMENT = '#' + S_INLINE = '%inline %{' + S_PYCODE_START = '%pythoncode %{' + + in_inline = False + in_pythoncode = False + in_pydoc = False + + for line in lines: + line = line.rstrip() + # skip empty lines + if not line: + continue + + # Inside pythoncode tag? + if in_pythoncode: + if line == S_PYDOC_START: + in_pydoc = True + continue + elif line == S_PYDOC_END: + in_pydoc = False + continue + elif line == S_SWIG_CLOSE: + in_pythoncode = False + continue + # Skip unneeded tags + elif line[:8] == '#