From 930d7cbcd4e6692b0dcabd11d5ba2ea0569455a4 Mon Sep 17 00:00:00 2001 From: "elias.bachaalany@gmail.com" Date: Fri, 2 Dec 2011 15:40:11 +0000 Subject: [PATCH] added PyWraps sources. This will facilitate deployment, development and debugging of IDAPython additions --- pywraps/deploy.bat | 125 ++ pywraps/deploy.py | 91 ++ pywraps/driver.cpp | 172 +++ pywraps/driver_bytes.cpp | 19 + pywraps/driver_chooser.cpp | 109 ++ pywraps/driver_cli.cpp | 29 + pywraps/driver_custdata.cpp | 77 ++ pywraps/driver_custview.cpp | 384 +++++++ pywraps/driver_dbg.cpp | 78 ++ pywraps/driver_diskio.cpp | 47 + pywraps/driver_expr.cpp | 56 + pywraps/driver_graph.cpp | 44 + pywraps/driver_kernwin.cpp | 78 ++ pywraps/driver_nalt.cpp | 18 + pywraps/driver_notifywhen.cpp | 36 + pywraps/link_gen.py | 336 ++++++ pywraps/py_appcall.py | 978 ++++++++++++++++ pywraps/py_askusingform.hpp | 355 ++++++ pywraps/py_askusingform.py | 1876 +++++++++++++++++++++++++++++++ pywraps/py_bytes.hpp | 218 ++++ pywraps/py_choose.hpp | 87 ++ pywraps/py_choose2.hpp | 956 ++++++++++++++++ pywraps/py_choose2.py | 390 +++++++ pywraps/py_cli.hpp | 293 +++++ pywraps/py_cli.py | 187 +++ pywraps/py_custdata.hpp | 699 ++++++++++++ pywraps/py_custdata.py | 241 ++++ pywraps/py_custview.hpp | 1110 ++++++++++++++++++ pywraps/py_custview.py | 458 ++++++++ pywraps/py_cvt.hpp | 1095 ++++++++++++++++++ pywraps/py_dbg.hpp | 699 ++++++++++++ pywraps/py_diskio.hpp | 62 + pywraps/py_diskio.py | 5 + pywraps/py_expr.hpp | 156 +++ pywraps/py_expr.py | 169 +++ pywraps/py_gdl.py | 88 ++ pywraps/py_graph.hpp | 816 ++++++++++++++ pywraps/py_graph.py | 169 +++ pywraps/py_idaapi.hpp | 804 +++++++++++++ pywraps/py_idaapi.py | 513 +++++++++ pywraps/py_idp.hpp | 1536 +++++++++++++++++++++++++ pywraps/py_kernwin.hpp | 902 +++++++++++++++ pywraps/py_kernwin.py | 110 ++ pywraps/py_lines.hpp | 211 ++++ pywraps/py_lines.py | 34 + pywraps/py_linput.hpp | 361 ++++++ pywraps/py_loader.hpp | 78 ++ pywraps/py_nalt.hpp | 486 ++++++++ pywraps/py_nalt.py | 191 ++++ pywraps/py_name.hpp | 23 + pywraps/py_name.py | 52 + pywraps/py_notifywhen.hpp | 307 +++++ pywraps/py_notifywhen.py | 63 ++ pywraps/py_plgform.hpp | 158 +++ pywraps/py_plgform.py | 108 ++ pywraps/py_qfile.hpp | 327 ++++++ pywraps/py_typeinf.hpp | 308 +++++ pywraps/py_ua.hpp | 793 +++++++++++++ pywraps/py_ua.py | 519 +++++++++ pywraps/pywraps.hpp | 2 + pywraps/pywraps.sln | 32 + pywraps/pywraps.vcproj | 1844 ++++++++++++++++++++++++++++++ pywraps/pywraps.vcxproj | 663 +++++++++++ pywraps/pywraps.vcxproj.filters | 518 +++++++++ pywraps/readme.txt | 116 ++ pywraps/sidaapi.py | 225 ++++ pywraps/sidc.py | 311 +++++ pywraps/swig_stub.cpp | 6 + pywraps/swig_stub.h | 27 + 69 files changed, 24434 insertions(+) create mode 100644 pywraps/deploy.bat create mode 100644 pywraps/deploy.py create mode 100644 pywraps/driver.cpp create mode 100644 pywraps/driver_bytes.cpp create mode 100644 pywraps/driver_chooser.cpp create mode 100644 pywraps/driver_cli.cpp create mode 100644 pywraps/driver_custdata.cpp create mode 100644 pywraps/driver_custview.cpp create mode 100644 pywraps/driver_dbg.cpp create mode 100644 pywraps/driver_diskio.cpp create mode 100644 pywraps/driver_expr.cpp create mode 100644 pywraps/driver_graph.cpp create mode 100644 pywraps/driver_kernwin.cpp create mode 100644 pywraps/driver_nalt.cpp create mode 100644 pywraps/driver_notifywhen.cpp create mode 100644 pywraps/link_gen.py create mode 100644 pywraps/py_appcall.py create mode 100644 pywraps/py_askusingform.hpp create mode 100644 pywraps/py_askusingform.py create mode 100644 pywraps/py_bytes.hpp create mode 100644 pywraps/py_choose.hpp create mode 100644 pywraps/py_choose2.hpp create mode 100644 pywraps/py_choose2.py create mode 100644 pywraps/py_cli.hpp create mode 100644 pywraps/py_cli.py create mode 100644 pywraps/py_custdata.hpp create mode 100644 pywraps/py_custdata.py create mode 100644 pywraps/py_custview.hpp create mode 100644 pywraps/py_custview.py create mode 100644 pywraps/py_cvt.hpp create mode 100644 pywraps/py_dbg.hpp create mode 100644 pywraps/py_diskio.hpp create mode 100644 pywraps/py_diskio.py create mode 100644 pywraps/py_expr.hpp create mode 100644 pywraps/py_expr.py create mode 100644 pywraps/py_gdl.py create mode 100644 pywraps/py_graph.hpp create mode 100644 pywraps/py_graph.py create mode 100644 pywraps/py_idaapi.hpp create mode 100644 pywraps/py_idaapi.py create mode 100644 pywraps/py_idp.hpp create mode 100644 pywraps/py_kernwin.hpp create mode 100644 pywraps/py_kernwin.py create mode 100644 pywraps/py_lines.hpp create mode 100644 pywraps/py_lines.py create mode 100644 pywraps/py_linput.hpp create mode 100644 pywraps/py_loader.hpp create mode 100644 pywraps/py_nalt.hpp create mode 100644 pywraps/py_nalt.py create mode 100644 pywraps/py_name.hpp create mode 100644 pywraps/py_name.py create mode 100644 pywraps/py_notifywhen.hpp create mode 100644 pywraps/py_notifywhen.py create mode 100644 pywraps/py_plgform.hpp create mode 100644 pywraps/py_plgform.py create mode 100644 pywraps/py_qfile.hpp create mode 100644 pywraps/py_typeinf.hpp create mode 100644 pywraps/py_ua.hpp create mode 100644 pywraps/py_ua.py create mode 100644 pywraps/pywraps.hpp create mode 100644 pywraps/pywraps.sln create mode 100644 pywraps/pywraps.vcproj create mode 100644 pywraps/pywraps.vcxproj create mode 100644 pywraps/pywraps.vcxproj.filters create mode 100644 pywraps/readme.txt create mode 100644 pywraps/sidaapi.py create mode 100644 pywraps/sidc.py create mode 100644 pywraps/swig_stub.cpp create mode 100644 pywraps/swig_stub.h diff --git a/pywraps/deploy.bat b/pywraps/deploy.bat new file mode 100644 index 0000000..8d222d8 --- /dev/null +++ b/pywraps/deploy.bat @@ -0,0 +1,125 @@ +@echo off + +rem Please use the same tag for the same .i file +rem That means if many insertions are going to happen in one given .i file then don't use more than code marking tag + +set PY=c:\python26\python.exe + +echo. +echo -------- DEPLOY started -------------------------------------------------- +echo. + +rem -------------------------------------------------------------------------- +echo Deploying idaapi (common functions, notifywhen) +%PY% deploy.py py_idaapi py_cvt.hpp,py_idaapi.hpp,py_idaapi.py,py_notifywhen.hpp,py_notifywhen.py ..\swig\idaapi.i + +rem -------------------------------------------------------------------------- +echo Deploying Graph +%PY% deploy.py py_graph py_graph.hpp,py_graph.py ..\swig\graph.i + +rem -------------------------------------------------------------------------- +echo Deploying custview +%PY% deploy.py py_custviewer py_custview.py,py_custview.hpp ..\swig\kernwin.i + +rem -------------------------------------------------------------------------- +echo Deploying plgform +%PY% deploy.py py_plgform py_plgform.hpp,py_plgform.py ..\swig\kernwin.i + +rem -------------------------------------------------------------------------- +echo Deploying expr +%PY% deploy.py py_expr py_expr.hpp,py_expr.py ..\swig\expr.i + +rem -------------------------------------------------------------------------- +echo Deploying cli +%PY% deploy.py py_cli py_cli.py,py_cli.hpp ..\swig\kernwin.i + +rem -------------------------------------------------------------------------- +echo Deploying Loader +%PY% deploy.py py_loader py_loader.hpp ..\swig\loader.i + +rem -------------------------------------------------------------------------- +echo Deploying kernwin, choose2, askusingform +%PY% deploy.py py_kernwin py_kernwin.hpp,py_kernwin.py,py_choose.hpp,py_choose2.hpp,py_choose2.py,py_askusingform.hpp,py_askusingform.py ..\swig\kernwin.i + +rem -------------------------------------------------------------------------- +echo Deploying idd +%PY% deploy.py py_idd py_dbg.hpp,py_appcall.py ..\swig\idd.i + +rem -------------------------------------------------------------------------- +echo Deploying nalt +%PY% deploy.py py_nalt py_nalt.hpp,py_nalt.py ..\swig\nalt.i + +rem -------------------------------------------------------------------------- +echo Deploying dbg +%PY% deploy.py py_dbg py_dbg.hpp ..\swig\dbg.i + +rem -------------------------------------------------------------------------- +echo Deploying linput/diskio +%PY% deploy.py py_diskio py_linput.hpp,py_diskio.hpp,py_diskio.py ..\swig\diskio.i + +rem -------------------------------------------------------------------------- +echo Deploying name +%PY% deploy.py py_name py_name.hpp,py_name.py ..\swig\name.i + +rem -------------------------------------------------------------------------- +echo Deploying qfile +%PY% deploy.py py_qfile py_qfile.hpp ..\swig\fpro.i + +rem -------------------------------------------------------------------------- +echo Deploying bytes +%PY% deploy.py py_bytes py_bytes.hpp,py_custdata.py,py_custdata.hpp ..\swig\bytes.i + +rem -------------------------------------------------------------------------- +echo Deploying typeinf +%PY% deploy.py py_typeinf py_typeinf.hpp ..\swig\typeinf.i + +rem -------------------------------------------------------------------------- +echo Deploying gdl +%PY% deploy.py py_gdl py_gdl.py ..\swig\gdl.i + +rem -------------------------------------------------------------------------- +echo Deploying ua +%PY% deploy.py py_ua py_ua.hpp,py_ua.py ..\swig\ua.i + +rem -------------------------------------------------------------------------- +echo Deploying idp +%PY% deploy.py py_idp py_idp.hpp ..\swig\idp.i + +rem -------------------------------------------------------------------------- +echo Deploying lines +%PY% deploy.py py_lines py_lines.hpp,py_lines.py ..\swig\lines.i + +rem -------------------------------------------------------------------------- +echo Deploying pc_win32_appcall +%PY% deploy.py appcalltest py_appcall.py ..\..\..\ida\tests\input\pc_win32_appcall.pe.hints + +rem -------------------------------------------------------------------------- +echo Deploying ex_custdata example +%PY% deploy.py ex_custdata ..\examples\ex_custdata.py ..\..\..\ida\tests\input\pc_win32_custdata1.pe.hints + +rem -------------------------------------------------------------------------- +echo Deploying ex_formchooser +%PY% deploy.py ex_formchooser py_askusingform.py ..\..\formchooser\formchooser.py + +rem -------------------------------------------------------------------------- +echo Deploying ex_askusingform +%PY% deploy.py ex_askusingform py_askusingform.py ..\examples\ex_askusingform.py + +rem -------------------------------------------------------------------------- +echo Deploying ex_cli example +%PY% deploy.py ex_cli_ex1 py_cli.py ..\examples\ex_cli.py + +rem -------------------------------------------------------------------------- +echo Deploying ex_expr example +%PY% deploy.py ex_expr py_expr.py ..\examples\ex_expr.py + +rem -------------------------------------------------------------------------- +echo Deploying ex_custview.py example +%PY% deploy.py py_custviewerex1 py_custview.py ..\examples\ex_custview.py + +rem -------------------------------------------------------------------------- +echo. +echo -------- DEPLOY finished ------------------------------------------------- +echo. + +:end \ No newline at end of file diff --git a/pywraps/deploy.py b/pywraps/deploy.py new file mode 100644 index 0000000..d883ea7 --- /dev/null +++ b/pywraps/deploy.py @@ -0,0 +1,91 @@ +""" +Deploy code snips into swig interface files + +(c) Hex-Rays +""" + +import sys +import re +import os + +# creates a regular expression +def make_re(tag, mod_name, prefix): + s = '%(p)s<%(tag)s\(%(m)s\)>(.+?)%(p)s' % {'m': mod_name, 'tag': tag, 'p': prefix} + return (s, re.compile(s, re.DOTALL)) + +def deploy(mod_name, src_files, dest_file, silent = True): + # create regular expressions + templates = ( + ('pycode', make_re('pycode', mod_name, '#')), + ('code', make_re('code', mod_name, '//')), + ('inline', make_re('inline', mod_name, '//')) + ) + + if not os.path.exists(dest_file): + print "File", dest_file, "does not exist and will be skipped" + return + + if not os.access(dest_file, os.W_OK): + print "File", dest_file, "is not writable and will be skipped" + return + + # read dest file + dest_lines = "".join(file(dest_file, "r").readlines()) + + # read all source files into one buffer + src_lines = "".join(["".join(file(x, "r").readlines()) for x in src_files]) + + pcount = 0 + for desc, (expr_str, expr) in templates: + # find source pattern + matches = expr.findall(src_lines) + if not matches: + if not silent: + print "Failed to match <%s> source expression against '%s', skipping...!" % (desc, expr_str) + continue + + # find pattern in destination + dest = expr.search(dest_lines) + if not dest: + if not silent: + print "Failed to match <%s> destination expression against '%s', skipping..." % (desc, expr_str) + print dest_lines + sys.exit(0) + continue + + # accumulate all the strings to be replaced + replaces = [] + for src in matches: + replaces.append(src) + + dest_lines = dest_lines[:dest.start(1)] + "\n".join(replaces) + dest_lines[dest.end(1):] + pcount += 1 + + + f = file(dest_file, 'w') + if not f: + print "Failed to open destination file:", dest_file + return + f.write(dest_lines) + f.close() + + if pcount: + print "Deployed successfully: %s (%d)" % (dest_file, pcount) + else: + print "Nothing was deployed in: %s" % dest_file + + +def main(argv = None): + if not argv: + argv = sys.argv + if len(argv) != 4: + print "Usage deploy.py modname src_file1,src_file2,... dest_file" + return + + mod_name = argv[1] + src_files = argv[2].split(',') + dest_file = argv[3] + deploy(mod_name, src_files, dest_file) + +#main(['', 'py_graph', 'py_graph.hpp,py_graph.py', 'graph.i']) +main() \ No newline at end of file diff --git a/pywraps/driver.cpp b/pywraps/driver.cpp new file mode 100644 index 0000000..09719d9 --- /dev/null +++ b/pywraps/driver.cpp @@ -0,0 +1,172 @@ +//-------------------------------------------------------------------------- +// IDA includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//-------------------------------------------------------------------------- +// PyWraps +#include +#include "pywraps.hpp" +#include "swig_stub.h" +#include "py_cvt.hpp" +#include "py_idaapi.hpp" +#include "py_graph.hpp" +#include "py_typeinf.hpp" +#include "py_bytes.hpp" +#include "py_linput.hpp" +#include "py_qfile.hpp" +#include "py_ua.hpp" +#include "py_custdata.hpp" +#include "py_notifywhen.hpp" +#include "py_dbg.hpp" +#include "py_choose2.hpp" +#include "py_plgform.hpp" +#include "py_cli.hpp" +#include "py_custview.hpp" +#include "py_lines.hpp" +#include "py_nalt.hpp" +#include "py_loader.hpp" +#include "py_idp.hpp" +#include "py_kernwin.hpp" +#include "py_askusingform.hpp" +#include "py_expr.hpp" + +//-------------------------------------------------------------------------- +qvector all_methods; +void driver_add_methods(PyMethodDef *methods) +{ + for ( ; methods->ml_name != NULL ; ++methods ) + all_methods.push_back(*methods); +} + +//-------------------------------------------------------------------------- +// Define a class and declare an instance so it gets executed on startup +// It will add the desired methods to the all_methods global variable +#define DRIVER_INIT_METHODS(name) \ + class init_##name##_driver_t \ + { \ + public: \ + init_##name##_driver_t() \ + { \ + driver_add_methods(py_methods_##name##); \ + } \ + } init_##name##_driver; + +//-------------------------------------------------------------------------- +// PyWraps test drivers +//#include "driver_kernwin.cpp" +//#include "driver_chooser.cpp" +#include "driver_expr.cpp" +//#include "driver_custview.cpp" +//#include "driver_notifywhen.cpp" +//#include "driver_custdata.cpp" +//#include "driver_graph.cpp" +//#include "driver_diskio.cpp" +//#include "driver_bytes.cpp" +//#include "driver_dbg.cpp" +//#include "driver_nalt.cpp" +//#include "driver_cli.cpp" + +//-------------------------------------------------------------------------- +//#define DRIVER_FIX + +#ifdef DRIVER_FIX + #define PLUGIN_FLAGS PLUGIN_FIX +#else + #define PLUGIN_FLAGS 0 +#endif + +//-------------------------------------------------------------------------- +void setup_pywraps() +{ + static bool installed = false; + if ( installed ) + { + msg("pywraps already installed\n"); + return; + } + static const PyMethodDef null_method = {0}; + all_methods.push_back(null_method); + Py_InitModule("pywraps", all_methods.begin()); + init_pywraps(); + msg("pywraps installed!\n"); + installed = true; +} + +//-------------------------------------------------------------------------- +void idaapi run(int /*arg*/) +{ + setup_pywraps(); +#ifdef DRIVER_RUN + driver_run(0); +#endif +} + +//-------------------------------------------------------------------------- +// +// Initialize. +// +int idaapi init(void) +{ +#ifndef DRIVER_FIX + setup_pywraps(); +#endif +#ifdef DRIVER_INIT + return driver_init(); +#else + return PLUGIN_KEEP; +#endif +} + +//-------------------------------------------------------------------------- +void idaapi term(void) +{ +#ifdef DRIVER_TERM + driver_term(); +#endif +} + +//-------------------------------------------------------------------------- +// +// PLUGIN DESCRIPTION BLOCK +// +//-------------------------------------------------------------------------- +plugin_t PLUGIN = +{ + IDP_INTERFACE_VERSION, + PLUGIN_FLAGS, // plugin flags + init, // initialize + + term, // terminate. this pointer may be NULL. + + run, // invoke plugin + + // long comment about the plugin + "PyWraps plugin", + + // it could appear in the status line + // or as a hint + "", // multiline help about the plugin + + "pywraps", // the preferred short name of the plugin + "Alt-0" // the preferred hotkey to run the plugin +}; diff --git a/pywraps/driver_bytes.cpp b/pywraps/driver_bytes.cpp new file mode 100644 index 0000000..4180bfe --- /dev/null +++ b/pywraps/driver_bytes.cpp @@ -0,0 +1,19 @@ +#include "py_bytes.hpp" + +//-------------------------------------------------------------------------- +static PyObject *ex_nextthat(PyObject *self, PyObject *args) +{ + PyObject *callback; + pyul_t addr, bound; + if ( !PyArg_ParseTuple(args, PY_FMT64 PY_FMT64 "O", &addr, &bound, &callback) ) + return NULL; + return Py_BuildValue("i", py_nextthat(pyul_t(addr), pyul_t(bound), callback)); +} + +//-------------------------------------------------------------------------- +static PyMethodDef py_methods_bytes[] = +{ + {"nextthat", ex_nextthat, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} +}; +DRIVER_INIT_METHODS(bytes); \ No newline at end of file diff --git a/pywraps/driver_chooser.cpp b/pywraps/driver_chooser.cpp new file mode 100644 index 0000000..618440c --- /dev/null +++ b/pywraps/driver_chooser.cpp @@ -0,0 +1,109 @@ +#include "py_choose2.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_find(PyObject *self, PyObject *args) +{ + char *title; + if ( !PyArg_ParseTuple(args, "s", &title) ) + return NULL; + else + return choose2_find(title); +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_create(PyObject *self, PyObject *args) +{ + PyObject *obj; + int embedded; + if ( !PyArg_ParseTuple(args, "Oi", &obj, &embedded) ) + return NULL; + else + return PyInt_FromLong(choose2_create(obj, embedded == 1 ? true : false)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_activate(PyObject *self, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + + choose2_activate(obj); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_close(PyObject *self, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + + choose2_close(obj); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_refresh(PyObject *self, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + + choose2_refresh(obj); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_add_command(PyObject *self, PyObject *args) +{ + PyObject *obj; + char *caption; + int flags, menu_index, icon; + if ( !PyArg_ParseTuple(args, "Osiii", &obj, &caption, &flags, &menu_index, &icon) ) + return NULL; + else + return PyInt_FromLong(choose2_add_command(obj, caption, flags, menu_index, icon)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_get_test_embedded(PyObject *self, PyObject *args) +{ + return PyLong_FromSize_t(choose2_get_test_embedded()); +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_get_embedded(PyObject *self, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + else + return choose2_get_embedded(obj); +} + +//------------------------------------------------------------------------- +static PyObject *ex_choose2_get_embedded_selection(PyObject *self, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + else + return choose2_get_embedded_selection(obj); +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_chooser[] = +{ + {"py_choose2_find", ex_choose2_find, METH_VARARGS, ""}, + {"py_choose2_create", ex_choose2_create, METH_VARARGS, ""}, + {"py_choose2_close", ex_choose2_close, METH_VARARGS, ""}, + {"py_choose2_activate", ex_choose2_activate, METH_VARARGS, ""}, + {"py_choose2_refresh", ex_choose2_refresh, METH_VARARGS, ""}, + {"py_choose2_add_command", ex_choose2_add_command, METH_VARARGS, ""}, + {"py_choose2_get_test_embedded", ex_choose2_get_test_embedded, METH_VARARGS, ""}, + {"py_choose2_get_embedded", ex_choose2_get_embedded, METH_VARARGS, ""}, + {"py_choose2_get_embedded_selection", ex_choose2_get_embedded_selection, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} // End of methods +}; +DRIVER_INIT_METHODS(chooser); diff --git a/pywraps/driver_cli.cpp b/pywraps/driver_cli.cpp new file mode 100644 index 0000000..92b24a1 --- /dev/null +++ b/pywraps/driver_cli.cpp @@ -0,0 +1,29 @@ +#include "py_custview.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_install_command_interpreter(PyObject *self, PyObject *args) +{ + PyObject *py_obj; + if ( !PyArg_ParseTuple(args, "O", &py_obj) ) + return NULL; + return PyInt_FromLong(py_install_command_interpreter(py_obj)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_remove_command_interpreter(PyObject *self, PyObject *args) +{ + int cli_idx; + if ( !PyArg_ParseTuple(args, "i", &cli_idx) ) + return NULL; + py_remove_command_interpreter(cli_idx); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_cli[] = +{ + {"install_command_interpreter", ex_install_command_interpreter, METH_VARARGS, ""}, + {"remove_command_interpreter", ex_remove_command_interpreter, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} +}; +DRIVER_INIT_METHODS(cli); \ No newline at end of file diff --git a/pywraps/driver_custdata.cpp b/pywraps/driver_custdata.cpp new file mode 100644 index 0000000..6f417da --- /dev/null +++ b/pywraps/driver_custdata.cpp @@ -0,0 +1,77 @@ +#include "py_custdata.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_register_custom_data_type(PyObject *self, PyObject *args) +{ + PyObject *py_dt; + if ( !PyArg_ParseTuple(args, "O", &py_dt) ) + return NULL; + return Py_BuildValue("i", py_register_custom_data_type(py_dt)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_unregister_custom_data_type(PyObject *self, PyObject *args) +{ + int dtid; + if ( !PyArg_ParseTuple(args, "i", &dtid) ) + return NULL; + return Py_BuildValue("i", py_unregister_custom_data_type(dtid)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_unregister_custom_data_format(PyObject *self, PyObject *args) +{ + int dtid, dfid; + if ( !PyArg_ParseTuple(args, "ii", &dtid, &dfid) ) + return NULL; + return Py_BuildValue("i", py_unregister_custom_data_format(dtid, dfid)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_register_custom_data_format(PyObject *self, PyObject *args) +{ + int dtid; + PyObject *py_df; + if ( !PyArg_ParseTuple(args, "iO", &dtid, &py_df) ) + return NULL; + return Py_BuildValue("i", py_register_custom_data_format(dtid, py_df)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_get_custom_data_format(PyObject *self, PyObject *args) +{ + int dtid, dfid; + if ( !PyArg_ParseTuple(args, "ii", &dtid, &dfid) ) + return NULL; + return py_get_custom_data_format(dtid, dfid); +} + +//------------------------------------------------------------------------- +static PyObject *ex_get_custom_data_type(PyObject *self, PyObject *args) +{ + int dtid; + if ( !PyArg_ParseTuple(args, "i", &dtid) ) + return NULL; + return py_get_custom_data_type(dtid); +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_custdata[] = +{ + {"unregister_custom_data_format", ex_unregister_custom_data_format, METH_VARARGS, ""}, + {"register_custom_data_format", ex_register_custom_data_format, METH_VARARGS, ""}, + {"unregister_custom_data_type", ex_unregister_custom_data_type, METH_VARARGS, ""}, + {"register_custom_data_type", ex_register_custom_data_type, METH_VARARGS, ""}, + {"get_custom_data_format", ex_get_custom_data_format, METH_VARARGS, ""}, + {"get_custom_data_type", ex_get_custom_data_type, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +//--------------------------------------------------------------------------- +class init_custdata_driver_t +{ +public: + init_custdata_driver_t() + { + driver_add_methods(py_methods_custdata); + } +} init_custdata_driver; diff --git a/pywraps/driver_custview.cpp b/pywraps/driver_custview.cpp new file mode 100644 index 0000000..66a9a05 --- /dev/null +++ b/pywraps/driver_custview.cpp @@ -0,0 +1,384 @@ +#include "py_custview.hpp" + +//-------------------------------------------------------------------------- +class my_custviewer: public customviewer_t +{ +private: + cvdata_simpleline_t data; + size_t id_n; + virtual bool on_popup_menu(size_t menu_id) + { + if ( menu_id == id_n ) + msg("popup menu N chosen!\n"); + return true; + } + virtual bool on_click(int shift) + { + msg("onclick; shift=%d\n", shift); + return true; + } + virtual void on_close() + { + id_n = 0; + msg("closed...\n"); + } + virtual bool on_keydown(int key, int shift) + { + switch ( key ) + { + case 'N': + warning("The hotkey 'N' has been pressed"); + return true; + case 'I': + { + int x, y; + place_t *pl = get_place(false, &x, &y); + if ( pl == NULL ) + return false; + msg("x=%d y=%d\n", x, y); + simpleline_t sl = *data.get_line(pl); + sl.bgcolor = bgcolor_t(~uint32(sl.bgcolor)); + data.set_line(data.to_lineno(pl), sl); + refresh_current(); + return true; + } + case 'A': + { + char buf[100]; + qsnprintf(buf, sizeof(buf), "This is line %d\n", data.count()); + data.add_line(buf); + msg("Added one more line...\n"); + return true; + } + case 'S': + { + twinpos_t p1, p2; + ::readsel2(_cv, &p1, &p2); + size_t y1 = data.to_lineno(p1.at); + size_t y2 = data.to_lineno(p2.at); + int x1 = p1.x; + int x2 = p2.x; + msg("(x1=%d y1=%d) (x2=%d y2=%d)", x1, y1, x2, y2); + return true; + } + case 'X': + data.set_minmax(); + return true; + case 'R': + refresh(); + msg("refreshing!\n"); + return true; + case IK_ESCAPE: + close(); + return true; + } + return false; + } + virtual void on_curpos_changed() + { + qstring word; + if ( get_current_word(false, word) ) + msg("Current word is: %s\n", word.c_str()); + } + virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) + { + simpleline_t *line = data.get_line(place); + if ( line == NULL ) + return false; + *important_lines = 1; + hint = line->line; + return true; + } + +public: + void init_sample_lines() + { + strvec_t &lines = data.get_lines(); + static struct + { + const char *text; + bgcolor_t color; + } const sample_lines[] = + { + { "This is a sample text", 0xFFFFFF }, + { "It will be displayed in the custom view", 0xFFC0C0 }, + { COLSTR("This line will be colored as erroneous", SCOLOR_ERROR), 0xC0FFC0 }, + { COLSTR("Every", SCOLOR_AUTOCMT) " " + COLSTR("word", SCOLOR_DNAME) " " + COLSTR("can", SCOLOR_IMPNAME) " " + COLSTR("be", SCOLOR_NUMBER) " " + COLSTR("colored!", SCOLOR_EXTRA), 0xC0C0FF }, + { " No limit on the number of lines.", 0xC0FFFF }, + }; + for ( int i=0; iinit("My sample viewer!") ) + { + msg("Failed to create cv\n!"); + return; + } + g_cv->show(); +} + +#define DRIVER_INIT +int driver_init() +{ + g_cv = new my_custviewer(); + return PLUGIN_KEEP; +} + +#define DRIVER_TERM +void driver_term() +{ + g_cv->close(); + delete g_cv; +} diff --git a/pywraps/driver_dbg.cpp b/pywraps/driver_dbg.cpp new file mode 100644 index 0000000..70704be --- /dev/null +++ b/pywraps/driver_dbg.cpp @@ -0,0 +1,78 @@ +#include "py_dbg.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_getthreadsregbase(PyObject * /*self*/, PyObject *args) +{ + PyObject *py_tid, *py_sreg_value; + if ( !PyArg_ParseTuple(args, "OO", &py_tid, &py_sreg_value) ) + return NULL; + return dbg_get_thread_sreg_base(py_tid, py_sreg_value); +} + +//------------------------------------------------------------------------- +static PyObject *ex_readmemory(PyObject * /*self*/, PyObject *args) +{ + PyObject *py_ea, *py_size; + if ( !PyArg_ParseTuple(args, "OO", &py_ea, &py_size) ) + return NULL; + return dbg_read_memory(py_ea, py_size); +} + +//------------------------------------------------------------------------- +static PyObject *ex_writememory(PyObject * /*self*/, PyObject *args) +{ + PyObject *py_ea, *py_buf; + if ( !PyArg_ParseTuple(args, "OO", &py_ea, &py_buf) ) + return NULL; + return dbg_write_memory(py_ea, py_buf); +} + +//------------------------------------------------------------------------- +static PyObject *ex_getmeminfo(PyObject * /*self*/, PyObject *args) +{ + return dbg_get_memory_info(); +} + +//------------------------------------------------------------------------- +static PyObject *ex_getregs(PyObject *self, PyObject *args) +{ + return dbg_get_registers(); +} + +//------------------------------------------------------------------------- +static PyObject *ex_appcall(PyObject * /*self*/, PyObject *args) +{ + PyObject *app_args, *type, *fields; + int func_ea, tid; + if ( !PyArg_ParseTuple(args, "iiOOO", &func_ea, &tid, &type, &fields, &app_args) ) + return NULL; + return py_appcall(func_ea, tid, type, fields, app_args); +} + +//------------------------------------------------------------------------- +static PyObject *ex_pytoidc( + PyObject *self, + PyObject *args) +{ + if ( !PyArg_ParseTuple(args, "O", &self) ) + return NULL; + idc_value_t v; + int sn = 0; + if ( pyvar_to_idcvar(self, &v, &sn) < CIP_OK ) + Py_RETURN_NONE; + Py_RETURN_TRUE; +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_dbg[] = +{ + {"getregs", ex_getregs, METH_VARARGS, ""}, + {"getmeminfo", ex_getmeminfo, METH_VARARGS, ""}, + {"readmemory", ex_readmemory, METH_VARARGS, ""}, + {"writememory", ex_writememory, METH_VARARGS, ""}, + {"getthreadsregbase", ex_getthreadsregbase, METH_VARARGS, ""}, + {"appcall", ex_appcall, METH_VARARGS, ""}, + {"pytoidc", ex_pytoidc, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +DRIVER_INIT_METHODS(dbg); \ No newline at end of file diff --git a/pywraps/driver_diskio.cpp b/pywraps/driver_diskio.cpp new file mode 100644 index 0000000..566541e --- /dev/null +++ b/pywraps/driver_diskio.cpp @@ -0,0 +1,47 @@ +#include "py_diskio.hpp" + +static PyObject *ex_enumfiles(PyObject * /*self*/, PyObject *args) +{ + PyObject *path, *fname, *callback; + if ( !PyArg_ParseTuple(args, "OOO", &path, &fname, &callback) ) + return NULL; + return py_enumerate_files(path, fname, callback); +} + +// +//static PyObject *ex_linput_close(PyObject * /*self*/, PyObject *args) +//{ +// PyObject *obj; +// if ( !PyArg_ParseTuple(args, "O", &obj) ) +// return NULL; +// pyl_close(obj); +// Py_RETURN_NONE; +//} +// +//static PyObject *ex_linput_open(PyObject *self, PyObject *args) +//{ +// PyObject *obj, *py_filename, *py_remote; +// if ( !PyArg_ParseTuple(args, "OOO", &obj, &py_filename, &py_remote) ) +// return NULL; +// return pyl_open(obj, py_filename, py_remote); +//} +// +//static PyObject *ex_linput_read(PyObject *self, PyObject *args) +//{ +// PyObject *obj, *py_size; +// if ( !PyArg_ParseTuple(args, "OO", &obj, &py_size) ) +// return NULL; +// return pyl_read(obj, py_size); +//} + +static PyMethodDef py_methods_diskio[] = +{ + {"enumfiles", ex_enumfiles, METH_VARARGS, ""}, + //{"tell", ex_linput_tell, METH_VARARGS, ""}, + //{"open", ex_linput_open, METH_VARARGS, ""}, + //{"size", ex_linput_tell, METH_VARARGS, ""}, + //{"read", ex_linput_read, METH_VARARGS, ""}, + //{"close", ex_linput_close, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +DRIVER_INIT_METHODS(diskio); \ No newline at end of file diff --git a/pywraps/driver_expr.cpp b/pywraps/driver_expr.cpp new file mode 100644 index 0000000..ef8db43 --- /dev/null +++ b/pywraps/driver_expr.cpp @@ -0,0 +1,56 @@ +#include "py_expr.hpp" + +#pragma warning(push) +#pragma warning(disable: 4244) + +//--------------------------------------------------------------------------- +static PyObject *ex_pyw_register_idc_func(PyObject *self, PyObject *args) +{ + char *name, *arg; + PyObject *py_fp; + if ( !PyArg_ParseTuple(args, "ssO", &name, &arg, &py_fp) ) + return NULL; + else + return PyLong_FromUnsignedLongLong(pyw_register_idc_func(name, arg, py_fp)); +} + +//--------------------------------------------------------------------------- +static PyObject *ex_pyw_unregister_idc_func(PyObject *self, PyObject *args) +{ + unsigned PY_LONG_LONG ctxptr; + if ( !PyArg_ParseTuple(args, "K", &ctxptr) ) + return NULL; + return PyLong_FromLong(pyw_unregister_idc_func(ctxptr)); +} + +static PyObject *ex_py_set_idc_func_ex(PyObject *self, PyObject *pyargs) +{ + const char *name; + unsigned PY_LONG_LONG fp_ptr; + const char *args; + int flags; + if ( !PyArg_ParseTuple(pyargs, "sKsi", &name, &fp_ptr, &args, &flags) ) + return NULL; + else + return PyLong_FromLong(py_set_idc_func_ex(name, fp_ptr, args, flags)); +} + +//--------------------------------------------------------------------------- +static PyObject *ex_py_get_call_idc_func(PyObject *self, PyObject *args) +{ + return PyLong_FromUnsignedLongLong(py_get_call_idc_func()); +} + +//------------------------------------------------------------------------- +#pragma warning(pop) + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_expr[] = +{ + {"pyw_register_idc_func", ex_pyw_register_idc_func, METH_VARARGS, ""}, + {"pyw_unregister_idc_func", ex_pyw_unregister_idc_func, METH_VARARGS, ""}, + {"py_get_call_idc_func", ex_py_get_call_idc_func, METH_VARARGS, ""}, + {"py_set_idc_func_ex", ex_py_set_idc_func_ex, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} // End of methods +}; +DRIVER_INIT_METHODS(expr); diff --git a/pywraps/driver_graph.cpp b/pywraps/driver_graph.cpp new file mode 100644 index 0000000..b44e232 --- /dev/null +++ b/pywraps/driver_graph.cpp @@ -0,0 +1,44 @@ +#include "py_graph.hpp" + +//-------------------------------------------------------------------------- +//py_choose2_t *last_c2 = NULL; +static PyObject *ex_graph_show(PyObject * /*self*/, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + + py_graph_t *ret = py_graph_t::Show(obj); + return PyBool_FromLong(ret == NULL ? 0 : 1); +} + +//-------------------------------------------------------------------------- +static PyObject *ex_graph_refresh(PyObject * /*self*/, PyObject *args) +{ + PyObject *obj; + if ( !PyArg_ParseTuple(args, "O", &obj) ) + return NULL; + py_graph_t::Refresh(obj); + Py_RETURN_NONE; +} + +//-------------------------------------------------------------------------- +static PyObject *ex_graph_addcmd(PyObject *self, PyObject *args) +{ + PyObject *obj; + const char *title, *hotkey; + if ( !PyArg_ParseTuple(args, "Oss", &obj, &title, &hotkey) ) + return NULL; + Py_ssize_t r = py_graph_t::AddCommand(obj, title, hotkey); + return Py_BuildValue("n", r); +} + +//-------------------------------------------------------------------------- +static PyMethodDef py_methods_graph[] = +{ + {"show", ex_graph_show, METH_VARARGS, ""}, + {"refresh", ex_graph_refresh, METH_VARARGS, ""}, + {"addcmd", ex_graph_addcmd, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +DRIVER_INIT_METHODS(graph); \ No newline at end of file diff --git a/pywraps/driver_kernwin.cpp b/pywraps/driver_kernwin.cpp new file mode 100644 index 0000000..6e3ea09 --- /dev/null +++ b/pywraps/driver_kernwin.cpp @@ -0,0 +1,78 @@ +#include "py_kernwin.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_add_menu_item(PyObject *self, PyObject *args) +{ + const char *menupath, *name, *hotkey; + PyObject *pyfunc, *pyargs; + int flags; + if ( !PyArg_ParseTuple(args, "sssiOO", &menupath, &name, &hotkey, &flags, &pyfunc, &pyargs) ) + return NULL; + return py_add_menu_item(menupath, name, hotkey, flags, pyfunc, pyargs); +} + +//------------------------------------------------------------------------- +static PyObject *ex_del_menu_item(PyObject *self, PyObject *args) +{ + if ( !PyArg_ParseTuple(args, "O", &self) ) + return NULL; + if ( py_del_menu_item(self) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +static PyObject *ex_execute_sync(PyObject *self, PyObject *args) +{ + PyObject *pycall; + int reqf; + if ( !PyArg_ParseTuple(args, "Oi", &pycall, &reqf) ) + return NULL; + return PyInt_FromLong(py_execute_sync(pycall, reqf)); +} + +//------------------------------------------------------------------------- +static PyObject *ex_add_hotkey(PyObject *self, PyObject *args) +{ + PyObject *pyfunc; + const char *hotkey; + if ( !PyArg_ParseTuple(args, "sO", &hotkey, &pyfunc) ) + return NULL; + else + return py_add_hotkey(hotkey, pyfunc); +} + +//------------------------------------------------------------------------- +static PyObject *ex_del_hotkey(PyObject *self, PyObject *args) +{ + PyObject *pyctx; + if ( !PyArg_ParseTuple(args, "O", &pyctx) ) + return NULL; + else + return PyInt_FromLong(py_del_hotkey(pyctx) ? 1 : 0); +} + +//------------------------------------------------------------------------- +static PyObject *ex_execute_ui_request(PyObject *self, PyObject *args) +{ + PyObject *py_list; + if ( !PyArg_ParseTuple(args, "O", &py_list) ) + return NULL; + else + return PyBool_FromLong(py_execute_ui_requests(py_list) ? 1 : 0); +} + + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_kernwin[] = +{ + {"py_del_menu_item", ex_del_menu_item, METH_VARARGS, ""}, + {"py_add_menu_item", ex_add_menu_item, METH_VARARGS, ""}, + {"py_execute_sync", ex_execute_sync, METH_VARARGS, ""}, + {"py_add_hotkey", ex_add_hotkey, METH_VARARGS, ""}, + {"py_del_hotkey", ex_del_hotkey, METH_VARARGS, ""}, + {"py_execute_ui_request", ex_execute_ui_request, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +DRIVER_INIT_METHODS(kernwin); \ No newline at end of file diff --git a/pywraps/driver_nalt.cpp b/pywraps/driver_nalt.cpp new file mode 100644 index 0000000..5ff9e56 --- /dev/null +++ b/pywraps/driver_nalt.cpp @@ -0,0 +1,18 @@ +#include "py_nalt.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_get_switch_info_ex(PyObject *self, PyObject *args) +{ + pyul_t ea; + if ( !PyArg_ParseTuple(args, PY_FMT64, &ea) ) + return NULL; + return py_get_switch_info_ex(ea_t(ea)); +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_nalt[] = +{ + {"get_switch_info_ex", ex_get_switch_info_ex, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} +}; +DRIVER_INIT_METHODS(nalt); diff --git a/pywraps/driver_notifywhen.cpp b/pywraps/driver_notifywhen.cpp new file mode 100644 index 0000000..1d44004 --- /dev/null +++ b/pywraps/driver_notifywhen.cpp @@ -0,0 +1,36 @@ +#include "py_notifywhen.hpp" + +//------------------------------------------------------------------------- +static PyObject *ex_notify_when(PyObject *self, PyObject *args) +{ + int when; + PyObject *py_callable; + if ( !PyArg_ParseTuple(args, "IO", &when, &py_callable) ) + return NULL; + return Py_BuildValue("i", notify_when(when, py_callable)); +} + +//------------------------------------------------------------------------- +static PyMethodDef py_methods_nw[] = +{ + {"notify_when", ex_notify_when, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +DRIVER_INIT_METHODS(nw); + +#define DRIVER_INIT +int driver_init() +{ + bool ok = pywraps_nw_init(); + if ( !ok ) + return PLUGIN_SKIP; + pywraps_nw_notify(NW_INITIDA_SLOT); + return PLUGIN_KEEP; +} + +#define DRIVER_TERM +void driver_term() +{ + pywraps_nw_notify(NW_TERMIDA_SLOT); + pywraps_nw_term(); +} diff --git a/pywraps/link_gen.py b/pywraps/link_gen.py new file mode 100644 index 0000000..fad012a --- /dev/null +++ b/pywraps/link_gen.py @@ -0,0 +1,336 @@ +import types + +C_TO_PY_CAST = { + 'b' : 'char', + 'i' : 'int', + 'H' : 'uint16', + 'h' : 'int16', + 'B' : 'uchar', +} +# -------------------------------------------------------------------------------------------- +class gen_fmt(object): + def __init__(self, fields, tp = None, bv = None, cast=None, cmt = None): + self.fields = fields + self.tp = tp + # Format to be passed to Py_BuildValue + if not bv: + self.bv = "XXX" + else: + if bv == "K": + self.bv = "PY_FMT64" + else: + self.bv = '"%s"' % bv + + if not cast: + if bv == "K": + cast = "pyul_t" + elif bv in C_TO_PY_CAST: + cast = C_TO_PY_CAST[bv] + + self.cast = "" if not cast else "(%s)" % cast + self.cmt = cmt + if bv == "K": + self.setcvt = "uint64 v(0); PyGetNumber(value, &v);" + elif bv == 'i': + self.setcvt = "int v = PyInt_AsLong(value);" + else: + self.setcvt = "uint64 v = %sPyInt_AsLong(value);" % self.cast + + +# -------------------------------------------------------------------------------------------- +switch_info_ex_t_gen = [ + gen_fmt('regdtyp', bv = 'b', cmt = 'size of the switch expression register as dtyp'), + gen_fmt('flags2', bv = 'i'), + gen_fmt('jcases', bv = 'i', cmt = 'number of entries in the jump table (SWI2_INDIRECT)'), + gen_fmt('regnum', bv = 'i', cmt = 'the switch expression as a register number'), + gen_fmt('flags', bv = 'H', cmt = 'the switch expression as a register number'), + gen_fmt('ncases', bv = 'H', cmt = 'number of cases (excluding default)'), + gen_fmt('defjump', bv = 'K', cmt = 'default jump address'), + gen_fmt('jumps', bv = 'K', cmt = 'jump table address'), + gen_fmt('elbase', bv = 'K', cmt = 'element base'), + gen_fmt('startea', bv = 'K', cmt = 'start of switch idiom'), + gen_fmt('custom', bv = 'K', cmt = 'information for custom tables (filled and used by modules)'), + gen_fmt('ind_lowcase', bv = 'K'), + gen_fmt(['values', 'lowcase'], bv = 'K'), +] + +op_t_gen = [ + gen_fmt('n', bv = 'b'), + gen_fmt('type', bv = 'B'), + gen_fmt('offb', bv = 'b'), + gen_fmt('offo', bv = 'b'), + gen_fmt('flags', bv = 'B'), + gen_fmt('dtyp', bv = 'b'), + gen_fmt(['reg', 'phrase'], bv = 'H'), + gen_fmt('value', bv = 'K'), + gen_fmt('addr', bv = 'K'), + gen_fmt('specval', bv = 'K'), + gen_fmt('specflag1', bv = 'b'), + gen_fmt('specflag2', bv = 'b'), + gen_fmt('specflag3', bv = 'b'), + gen_fmt('specflag4', bv = 'b') +] + +insn_t_gen = [ + gen_fmt('cs', bv = 'K'), + gen_fmt('ip', bv = 'K'), + gen_fmt('ea', bv = 'K'), + gen_fmt('itype', bv = 'H'), + gen_fmt('size', bv = 'H'), + gen_fmt('auxpref', bv = 'H'), + gen_fmt('segpref', bv = 'b'), + gen_fmt('insnpref', bv = 'b'), + gen_fmt('Op1', tp = 'op_t'), + gen_fmt('Op2', tp = 'op_t'), + gen_fmt('Op3', tp = 'op_t'), + gen_fmt('Op4', tp = 'op_t'), + gen_fmt('Op5', tp = 'op_t'), + gen_fmt('Op6', tp = 'op_t'), + gen_fmt('flags', bv = 'b') +] + +regval_t_gen = [ + gen_fmt('rvtype', bv = 'i'), + gen_fmt('ival', bv = 'K'), + gen_fmt('fval', bv = 'd'), + gen_fmt('bytes', bv = 's'), +] + +# -------------------------------------------------------------------------------------------- +S_LINK_ATTR = 'S_CLINK_NAME' # If the name is a literal, make sure you specify double quotations +S_CMOD_NAME = '_idaapi' + +# -------------------------------------------------------------------------------------------- +def gen_stub(gen, name, cname = None, tabs=4, gen_py_file = False, gen_c_file = False): + # Assume C type name same as python type name + if not cname: + cname = name + + # Python property lines + prop_body = [] + + # Python get/set bodies + getset_body = [] + + # C get/set bodies + cgetset_body = [] + + # some spacing constants + spc = ' ' * tabs + spc2 = spc * 2 + nspc = '\n' + spc + nspc2 = '\n' + spc2 + + cget_link = '%s_get_clink' % cname + + # + # Process fields + # + for g in gen: + # a union will be represented by a list + if type(g.fields) != types.ListType: + fields = [g.fields] + else: + fields = g.fields + + # join all field names (in case of a union) + flds_name = '_'.join(fields) + + # form the method and variable names + set_method = '__set_%s__' % flds_name + get_method = '__get_%s__' % flds_name + cset_method = '%s_set_%s' % (name, flds_name) + cget_method = '%s_get_%s' % (name, flds_name) + fld_name = '__%s__' % flds_name + + basic_type = not g.tp + + vars = { + 'get': get_method, + 'set': set_method, + 'l': S_LINK_ATTR, + 'fld' : fld_name, + 'cmod' : S_CMOD_NAME, + 'cget': cget_method, + 'cset': cset_method, + 'csetcvt': g.setcvt, + 'cname': cname, + 'cgetlink': cget_link, + 'cfield1': fields[0], + 'bv': g.bv, + 'bvcast': g.cast + } + + # + # Python code + # + + # basic type? + # For basic types we need to create property and get/set methods + if basic_type: + for fld in fields: + prop_body.append('%s = property(%s, %s)' % (fld, get_method, set_method)) + if g.cmt: + prop_body.append('"""%s"""' % g.cmt) + + # + code = '\n'.join([ + # get method + 'def %(get)s(self):', + spc2 + 'return %(cmod)s.%(cget)s(self)', + # set method + spc + 'def %(set)s(self, v):', + spc2 + '%(cmod)s.%(cset)s(self, v)', + ]) % vars + + getset_body.append(code) + + # + # C code + # + if basic_type: + code = '\n'.join([ +"""static PyObject *%(cget)s(PyObject *self) +{ + %(cname)s *link = %(cgetlink)s(self); + if ( link == NULL ) + Py_RETURN_NONE; + return Py_BuildValue(%(bv)s, %(bvcast)slink->%(cfield1)s); +} +static void %(cset)s(PyObject *self, PyObject *value) +{ + %(cname)s *link = %(cgetlink)s(self); + if ( link == NULL ) + return; + %(csetcvt)s + link->%(cfield1)s = %(bvcast)sv; +} + +""" + ]) % vars + + cgetset_body.append(code) + +# print 'prop_body->\n\t', '\n\t'.join(prop_body), '\n<' +# print 'getset_body->\n', '\n'.join(getset_body), '\n<' +# print 'cgetset_body->\n', '\n'.join(cgetset_body), '\n<' + + vars = { + 'name': name, + 'cname': cname, + 'getlink': cget_link, + 'l': S_LINK_ATTR, + 'cmod' : S_CMOD_NAME + } + + # + # Form the complete Python code + # + py = '\n'.join([ + 'class %(name)s(py_clinked_object_t):', + + # init() code + spc + 'def __init__(self, lnk = None):', + spc2 + 'py_clinked_object_t.__init__(self, lnk)', + '', + spc + 'def _create_clink(self):', + spc2 + 'return _idaapi.%(name)s_create()', + '', + spc + 'def _del_clink(self, lnk):', + spc2 + 'return _idaapi.%(name)s_destroy(lnk)', + '', + spc + 'def assign(self, other):', + spc2 + 'return _idaapi.%(name)s_assign(self, other)', + '', + '', + spc + '#', + spc + '# Autogenerated', + spc + '#', + # get/set code + spc + nspc.join(getset_body), + # props code + spc + nspc.join(prop_body), + ]) % vars + + # + # Form the Python to C conversion function + # + + # + # Form the complete C code + # + ccode = '\n'.join([ + # Form the C get link code +"""%(cname)s *%(getlink)s(PyObject *self) +{ + if ( !PyObject_HasAttrString(self, %(l)s) ) + return NULL; + %(cname)s *r; + PyObject *attr = PyObject_GetAttrString(self, %(l)s); + if ( PyCObject_Check(attr) ) + r = (%(cname)s *) PyCObject_AsVoidPtr(attr); + else + r = NULL; + Py_DECREF(attr); + return r; +} + +static PyObject *%(cname)s_create() +{ + %(cname)s *inst = new %(cname)s(); + return PyCObject_FromVoidPtr(inst, NULL); +} + +static bool %(cname)s_destroy(PyObject *py_obj) +{ + if ( !PyCObject_Check(py_obj) ) + return false; + %(cname)s *inst = (%(cname)s *) PyCObject_AsVoidPtr(py_obj); + delete inst; + return true; +} + +static bool %(cname)s_assign(PyObject *self, PyObject *other) +{ + %(cname)s *lhs = %(cname)s_get_clink(self); + %(cname)s *rhs = %(cname)s_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +// Auto generated - begin +// +""", + # Form C get/set functions + ''.join(cgetset_body), +"""// +// Auto generated - end +// +//-------------------------------------------------------------------------""" + ]) % vars + + # write the Python file + if gen_py_file: + f = open(name + '.py', 'w') + f.write(py) + f.close() + + # write C file + if gen_c_file: + f = open(name + '.cpp', 'w') + f.write(ccode) + f.close() + +# -------------------------------------------------------------------------------------------- +def main(): + files = [ + ('switch_info_ex_t', switch_info_ex_t_gen), + ] + for (n, g) in files: + gen_stub(g, n, gen_py_file = True, gen_c_file = True) + +main() \ No newline at end of file diff --git a/pywraps/py_appcall.py b/pywraps/py_appcall.py new file mode 100644 index 0000000..18dc316 --- /dev/null +++ b/pywraps/py_appcall.py @@ -0,0 +1,978 @@ +try: + import pywraps + pywraps_there = True +except: + pywraps_there = False +import _idaapi +import random +import operator +import datetime + +if pywraps_there: + _idaapi.appcall = pywraps.appcall + from py_idaapi import * +else: + import idaapi + from idaapi import * + +# ---------------------------------------------------------------------------------------------------------------------------------------------- +# +import types + +# ----------------------------------------------------------------------- +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 tuple must be passed" + self.__size = len(L) + if self.__size == 1: + self.__typedobj = Appcall__.typedobj(self.__type + ";") + else: + self.__typedobj = Appcall__.typedobj("%s x[%d];" % (self.__type, self.__size)) + # Now store the object in a string buffer + ok, buf = self.__typedobj.store(L) + if ok: + return Appcall__.byref(buf) + else: + 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" + # let us convert to a Python list + 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 + + # we can only unpack from strings + if type(buf) != types.StringType: + raise ValueError, "Cannot unpack this type!" + # now unpack + ok, obj = self.__typedobj.retrieve(buf) + if not ok: + raise ValueError, "Failed while unpacking!" + if not as_list: + return obj + return self.try_to_convert_to_list(obj) + + +# ----------------------------------------------------------------------- +# Wrapper class for the appcall() +class Appcall_callable__(object): + """ + Helper class to issue appcalls using a natural syntax: + appcall.FunctionNameInTheDatabase(arguments, ....) + or + appcall["Function@8"](arguments, ...) + or + f8 = appcall["Function@8"] + f8(arg1, arg2, ...) + or + o = appcall.obj() + i = byref(5) + appcall.funcname(arg1, i, "hello", o) + """ + def __init__(self, ea, tp = None, fld = None): + """Initializes an appcall with a given function ea""" + self.__ea = ea + 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 + + 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()""" + if self.ea is None: + raise ValueError, "Object not callable!" + + # convert arguments to a list + arg_list = list(args) + + # Save appcall options and set new global options + old_opt = Appcall__.get_appcall_options() + Appcall__.set_appcall_options(self.options) + + # Do the Appcall (use the wrapped version) + e_obj = None + try: + r = _idaapi.appcall( + self.ea, + _idaapi.get_current_thread(), + self.type, + self.fields, + arg_list) + except Exception as e: + e_obj = e + + # Restore appcall options + Appcall__.set_appcall_options(old_opt) + + # Return or re-raise exception + if e_obj: + raise Exception, e_obj + + return r + + def __get_ea(self): + return self.__ea + + def __set_ea(self, val): + self.__ea = val + + ea = property(__get_ea, __set_ea) + """Returns or sets the EA associated with this object""" + + def __get_size(self): + if self.__type == None: + return -1 + r = _idaapi.get_type_size0(_idaapi.cvar.idati, self.__type) + if not r: + return -1 + return r + + size = property(__get_size) + """Returns the size of the type""" + + def __get_type(self): + return self.__type + + type = property(__get_type) + """Returns the typestring""" + + def __get_fields(self): + return self.__fields + + fields = property(__get_fields) + """Returns the field names""" + + + def retrieve(self, src=None, flags=0): + """ + Unpacks a typed object from the database if an ea is given or from a string if a string was passed + @param src: the address of the object or a string + @return: Returns a tuple of boolean and object or error number (Bool, Error | Object). + """ + + # Nothing passed? Take the address and unpack from the database + if src is None: + src = self.ea + + if type(src) == types.StringType: + return _idaapi.unpack_object_from_bv(_idaapi.cvar.idati, self.type, self.fields, src, flags) + else: + return _idaapi.unpack_object_from_idb(_idaapi.cvar.idati, self.type, self.fields, src, flags) + + def store(self, obj, dest_ea=None, base_ea=0, flags=0): + """ + Packs an object into a given ea if provided or into a string if no address was passed. + @param obj: The object to pack + @param dest_ea: If packing to idb this will be the store location + @param base_ea: If packing to a buffer, this will be the base that will be used to relocate the pointers + + @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 + if dest_ea is None: + return _idaapi.pack_object_to_bv(obj, + _idaapi.cvar.idati, + self.type, + self.fields, + base_ea, + flags) + else: + return _idaapi.pack_object_to_idb(obj, + _idaapi.cvar.idati, + self.type, + self.fields, + dest_ea, + flags) + +# ----------------------------------------------------------------------- +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 + + def __getattr__(self, attr): + return Appcall__.valueof(attr, self.__default) + +# ----------------------------------------------------------------------- +class Appcall__(object): + APPCALL_MANUAL = 0x1 + """ + Only set up the appcall, do not run it. + you should call CleanupAppcall() when finished + """ + + 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_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 + Consts = property(__get_consts) + """Use Appcall.Consts.CONST_NAME to access constants""" + + @staticmethod + def __name_or_ea(name_or_ea): + """ + Function that accepts a name or an ea and checks if the address is enabled. + If a name is passed then idaapi.get_name_ea() is applied to retrieve the name + @return: + - Returns the resolved EA or + - Raises an exception if the address is not enabled + """ + + # a string? try to resolve it + if type(name_or_ea) == types.StringType: + ea = _idaapi.get_name_ea(_idaapi.BADADDR, name_or_ea) + else: + ea = name_or_ea + # could not resolve name or invalid address? + if ea == _idaapi.BADADDR or not _idaapi.isEnabled(ea): + raise ValueError, "Undefined function " + name_or_ea + return ea + + @staticmethod + def proto(name_or_ea, prototype, flags = None): + """ + Allows you to instantiate an appcall (callable object) with the desired prototype + @param name_or_ea: The name of the function (will be resolved with LocByName()) + @param prototype: + @return: + - On failure it raises an exception if the prototype could not be parsed + or the address is not resolvable + - Returns a callbable Appcall instance with the given prototypes and flags + """ + + # resolve and raise exception on error + ea = Appcall__.__name_or_ea(name_or_ea) + # parse the type + if flags is None: + flags = 1 | 2 | 4 # PT_SIL | PT_NDC | PT_TYP + + result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, prototype, flags) + if result is None: + raise ValueError, "Could not parse type: " + prototype + + # Return the callable method with type info + 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 (by returning a callable object)""" + # resolve and raise exception on error + ea = self.__name_or_ea(name_or_ea) + if ea == _idaapi.BADADDR: + raise ValueError, "Undefined function " + name + # Return the callable method + return Appcall_callable__(ea) + + def __getitem__(self, idx): + """ + Use self[func_name] syntax if the function name contains invalid characters for an attribute name + See __getattr___ + """ + return self.__getattr__(idx) + + @staticmethod + def valueof(name, default=0): + """ + Returns the numeric value of a given name string. + If the name could not be resolved then the default value will be returned + """ + t, v = _idaapi.get_name_value(_idaapi.BADADDR, name) + if t == 0: # NT_NONE + v = default + return v + + @staticmethod + def int64(v): + """Whenever a 64bit number is needed use this method to construct an object""" + return PyIdc_cvt_int64__(v) + + @staticmethod + def byref(val): + """ + Method to create references to immutable objects + Currently we support references to int/strings + Objects need not be passed by reference (this will be done automatically) + """ + return PyIdc_cvt_refclass__(val) + + @staticmethod + def buffer(str = None, size = 0, fill="\x00"): + """ + Creates a string buffer. The returned value (r) will be a byref object. + Use r.value to get the contents and r.size to get the buffer's size + """ + if str is None: + str = "" + left = size - len(str) + if left > 0: + str = str + (fill * left) + r = Appcall__.byref(str) + r.size = size + return r + + @staticmethod + def obj(**kwds): + """Returns an empty object or objects with attributes as passed via its keywords arguments""" + return object_t(**kwds) + + @staticmethod + def cstr(val): + return as_cstr(val) + + @staticmethod + def unicode(s): + return as_unicode(s) + + @staticmethod + def array(type_name): + """Defines an array type. Later you need to pack() / unpack()""" + return Appcall_array__(type_name) + + @staticmethod + def typedobj(typestr, ea=None): + """ + Parses a type string and returns an appcall object. + One can then use retrieve() member method + @param ea: Optional parameter that later can be used to retrieve the type + @return: Appcall object or raises ValueError exception + """ + # parse the type + result = _idaapi.idc_parse_decl(_idaapi.cvar.idati, typestr, 1 | 2 | 4) # PT_SIL | PT_NDC | PT_TYP + if result is None: + raise ValueError, "Could not parse type: " + typestr + # Return the callable method with type info + return Appcall_callable__(ea, result[1], result[2]) + + @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 + def cleanup_appcall(tid = 0): + """Equivalent to IDC's CleanupAppcall()""" + return _idaapi.cleanup_appcall(tid) + +Appcall = Appcall__() +# + +# ---------------------------------------------------------------------------------------------------------------------------------------------- + +# +a = Appcall # Take a shortcut to Appcall +c = Appcall.Consts # take shortcut to constants + +# ----------------------------------------------------------------------- +# - Adds missing types +# - ... +def init(): + # add neeeded types + if a.valueof("ERROR_FILE_NOT_FOUND") == 0: + add_type("MACRO_ERROR") + + setattr(c, "ERROR_FILE_NOT_FOUND", 2) + + if a.valueof("MEM_COMMIT") == 0: + add_type("MACRO_PAGE") + + # open log file + global test_log + try: + test_log = file("python_test.log", "a") + except: + test_log = None + +# ----------------------------------------------------------------------- +def deinit(): + global test_log + test_log.close() + test_log = None + return 1 + +# ----------------------------------------------------------------------- +def logprint(s): + if not test_log: + print s + else: + test_log.write(s + "\n") + return 1 + +# ----------------------------------------------------------------------- +def add_type(t): + print "adding type:", t + idaapi.import_type(idaapi.cvar.idati, 0, t) + +# ----------------------------------------------------------------------- +def test_init(): + hmod = getmodulehandlew(a.unicode("kernel32.dll")) + print "k32hmod=%x" % hmod + if hmod == 0: + return -1 + p = getprocaddr(hmod, "VirtualAlloc") + if p == 0: + return -2 + print "VirtualAlloc->%x" % p + virtualalloc.ea = p + + p = getprocaddr(hmod, "VirtualFree") + if p == 0: + return -3 + print "VirtualFree->%x" % p + virtualfree.ea = p + + m = virtualalloc(0, c.MEM_COMMIT, 0x1000, c.PAGE_EXECUTE_READWRITE) + idc.RefreshDebuggerMemory() + print "%x: allocated memory\n" % m + global WRITE_AREA + WRITE_AREA = m + + return 1 + +# ----------------------------------------------------------------------- +def test_deinit(): + virtualfree(WRITE_AREA, 0, c.MEM_FREE) + +# ----------------------------------------------------------------------- +# Tests changedir/setdir/buffer creation (two methods) and cstr() +def test_enum_files(): + # create a buffer + savedpath = a.byref("\x00" * 260) + # get current directory + n = getcurdir(250, savedpath) + out = [] + out.append("curdir=%s" % savedpath.value[0:n]) + + # get windir + windir = a.buffer(size=260) # create a buffer using helper function + n = getwindir(windir, windir.size) + if n == 0: + return -1 # could not get current directory + + windir = windir.value[:n] + out.append("windir=%s" % windir) + + # change to windows folder + setcurdir(windir) + + # initiate find + fd = a.obj() + h = findfirst("*.exe", fd) + if h == -1: + return -2 # no files found! + + found = -6 + while True: + fn = a.cstr(fd.cFileName) + if "regedit" in fn: + found = 1 + out.append("fn=%s<" % fn) + fd = a.obj() # reset the FD object + ok = findnext(h, fd) + if not ok: + break + # + findclose(h) + + # restore cur dir + setcurdir(savedpath.value) + + # verify + t = a.buffer(size=260) + n = getcurdir(t.size, t) + if t.cstr() != savedpath.cstr(): + return -4 # could not restore cur dir + + out.append("curdir=%s<" % t.cstr()) +# print "all done!" +# for l in out: +# print l + + return found +# ----------------------------------------------------------------------- +def test_gpa(): + h = loadlib("user32.dll") + if h == 0: + print "failed to load library!" + return -1 + p = getprocaddr(h, "FindWindowA") + if p == 0: + print "failed to gpa!" + return -2 + findwin = a.proto(p, "int FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName);") + hwnd = findwin("TIdaWindow", 0) + freelib(h) + print "%x: ok!->hwnd=%x" % (p, hwnd) + + return 1 + +# ----------------------------------------------------------------------- +# Packs a simple structure (into the database) and unpacks it back using the idaapi methods +def test_pck_idb_raw(): + name, tp, flds = idc.ParseType("struct { int a, b; char x[5];};", 0) + o = a.obj(a=15, b=17,x="hi") + idaapi.pack_object_to_idb(o, idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) + + ok, obj = idaapi.unpack_object_from_idb(idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) + if obj.a != 15 and obj.b != 17 and obj != "hi": + return -2 + return 1 + +# ----------------------------------------------------------------------- +# Packs a simple structure (into a string) and unpacks it back using the idaapi methods +def test_pck_bv_raw(): + name, tp, flds = idc.ParseType("struct { int a; char x[5]; int b;};", 0) + va,vb,vx = 15,17,"hi" + o = a.obj(a=va, b=vb,x=vx) + ok, s = idaapi.pack_object_to_bv(o, idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) + if not ok: + return -1 + ok, obj = idaapi.unpack_object_from_idb(idaapi.cvar.idati, tp, flds, WRITE_AREA, 0) + if obj.a != va and obj.b != vb and obj.x != vx: + return -2 + return 1 + +# ----------------------------------------------------------------------- +# 1. Unpacks the DOS header at 0x400000 and verify the fields +# 2. Unpacks a string and see if it is unpacked correctly +def test_unpack_raw(): + name, tp, flds = idc.ParseType("IMAGE_DOS_HEADER;", 0) + ok, obj = idaapi.unpack_object_from_idb(idaapi.cvar.idati, tp, flds, 0x400000, 0) + if obj.e_magic != 23117 and obj.e_cblp != 144: + return -1 + + name, tp, flds = idc.ParseType("struct abc_t { int a, b;};", 0) + ok, obj = idaapi.unpack_object_from_bv(idaapi.cvar.idati, tp, flds, "\x01\x00\x00\x00\x02\x00\x00\x00", 0) + if obj.a != 1 and obj.b != 2: + return -2 + + return 1 + +# ----------------------------------------------------------------------- +# Packs/Unpacks a structure to the database using appcall facilities +def test_pck_idb(): + print "%x: ..." % WRITE_AREA + tp = a.typedobj("struct { int a, b; char x[5];};") + o = a.obj(a=16, b=17,x="zzzhi") + if tp.store(o, WRITE_AREA) != 0: + return -1 + idc.RefreshDebuggerMemory() + + ok, r = tp.retrieve(WRITE_AREA) + if not ok: + return -2 + if r.a != o.a and r.b != o.b and r.x != o.x: + return -3 + + return 1 + +# ----------------------------------------------------------------------- +# Packs/Unpacks a structure to/from a string +def test_pck_bv(): + tp = a.typedobj("struct { int a, b; char x[5];};") + o = a.obj(a=16, b=17,x="zzzhi") + ok, packed = tp.store(o) + if not ok: + return -1 + print "packed->", repr(packed) + ok, r = tp.retrieve(packed) + if not ok: + return -2 + + if r.a != o.a and r.b != o.b and r.x != o.x: + return -3 + + return 1 + +# ----------------------------------------------------------------------- +# various tests +def test1(stage): + # call a method that takes a string buffer and appends a dot to its end + if stage == st_ref2: + buf = a.buffer("test", 100) + vals = [378, 424, 470] + for i in xrange(0, 2+1): + n = a._ref2(buf) + if buf.value[4+i] != '.': + return -st_ref2 + if vals[i] != n: + return -stage + # call a method that takes an integer reference + elif stage == st_ref1: + v = 5 + i = a.byref(v) + a._ref1(i) + if v + 1 != i.value: + return -stage + # call a method that takes an array of integers + elif stage == st_ref3: + # create an array type + arr = a.array("int") + # create a list + L = [x for x in xrange(1, 10)] + # pack the list + p_list = arr.pack(L) + # appcall to compute the total + c_total = a._ref3(p_list, len(L)) + # internally compute the total + total = reduce(operator.add, L) + if total != c_total: + return -stage + # subst() + elif stage == st_subst: + v = a._subst(5, 1) + if v != 4: + return -stage + v = subst(5, 1) # subst() / pascal + if v != -4: + return -stage*2 + elif stage == st_make2: + x = a._str2_make(5) + s = a.cstr(x.next.str) +# print "len=%d;<%s>" % (len(s), s) + if s != "This is string 2": + return -stage + n = a._str2_print(x) + if n != 5: + return -st_make2*2 + elif stage == st_make1: + x = a._str1_make(6) + if x.val != 1 and x.next.val != 2: + return -st_make1 + n = a._str1_print(x) + if n != 6: + return -stage + # 64bit test + elif stage == st_make3: + global gr + try: + x = a._str3_make(6) + gr = x + except Exception as e: + print "Exception: ", str(e) + return -stage + print "x.val32=", x.val32 + if (x.val32 != 1) and (x.val64 != (1 << 32)): + return -stage * 2 + va = 0 + vb = 0 + r = x + i = 0 + while x != 0: + i += 1 + print "1" + va += x.val32 + print "2" + vb += x.val64.value + print "3" + x = x.next + print "i=", i, "a=", va, "b=", vb + if va != 21 and vb != 90194313216: + return -stage*3 + elif stage == st_asm: + n = asm1(5, 1) + if n != 4: + return -stage + n = asm2(5, 1, 1) + if n != 7: + return -stage*2 + elif stage == st_byvalref1: + v1 = a.obj(val=5, next=0) + v2 = a.obj(str="Hello", next=0) + n = a._byvalref1(v1, 1, v2) + if n != 78: + return -stage + # v1 is passed by ref, thus it will be changed + if n + 1 != v1.val: + return -stage * 2 + elif stage == st_altsum: + # 1 + 2 - 3 + 5 = 5 + n = altsum(1, 2, 3, 5, 0) + if n != 5: + return -stage; + elif stage == st_op64: + # invalid opcode, should return -1 + r = a._op_two64(1, 2, 6).value + print "r=", r + if r != -1: + return -stage + r = a._op_two64(6, a.int64(2), 3).value + if r != 12: + return -stage * 2 + return 1 + elif stage == st_byval3: + o = a.obj(val=6,next=a.obj(val=-1, next=0)) +# print "before: o.val=%d o.next.val=%d" % (o.val, o.next.val) + n = a._byval3(o) + if n != 5: + return -stage +# print "after: o.val=%d o.next.val=%d, n=%d" % (o.val, o.next.val, n) + #--------- + elif stage == st_ex: + def doit(): + try: + # causes a DebugBreak() + a._ex2(1) + except Exception as e: + if not "Software breakpoint" in e.message: + return -stage + try: + a._ex1(1, 2) + return -st_ex * 2 + except Exception as e: + if not "referenced memory" in e.message: + return -stage + return 1 + old = a.set_appcall_options(0) + r = doit() + a.set_appcall_options(old) + if r <= 0: + return r + #--------- + elif stage == st_ref4: + i = a.int64(5) + v = a.byref(i) + if a._ref4(v) != 1: + return -st_ref4 + # test (in case recycling failed) + if v.value.value != 6: + return -st_ref4 * 2 + # test recycling + if i.value != 6: + return -st_ref4 * 3 + # return success + return 1 + +# ----------------------------------------------------------------------- +def test2(): + fn = "pc_win32_appcall.pe" + + # Try to open an non-existing file + h = a._file_open(fn + ".1") + if h != 0: + return -1 + + n = getlasterror() + print "gle=", n + if n != c.ERROR_FILE_NOT_FOUND: + return -2 + + # Should succeed + h = a._file_open(fn) + if h == 0: + return -3 + + s = a.buffer("", 10, "!") + n = a._file_read(h, s, 2) +# print "read=%d; buf=%s" % (n, repr(s.value)) + if s.value[:2] != "MZ": + return -4 + + n = a._file_close(h) + if n != h-1: + return -5 + + return 1 + +# ----------------------------------------------------------------------- +# This test changes the appcall options and sees if the appcall +# generates an exception and if we get it properly +# An appcall can throw an exception: +# - ValueError: conversion from/to idc/py failed +# - OSError: an OSError when APPCALL_DEBEV in Appcall_Options. In that case check the exc.args[0] to get the debug event +# - Exception: in all other cases +def test_exec_throw(): + old = a.set_appcall_options(0) + print "old_opt=", old, "new=0" + try: + # causes a divide by zero exception (will be reported as an Exception) + print a._op_two64(2,0,4).value + return -1 + except Exception as e: + print "runtime error!" + + # now appcall exceptions will be reported as OSErr and other exceptions as Exception + print "old_opt=", a.set_appcall_options(a.APPCALL_DEBEV), "new=2" + try: + # causes an error: Wrong number of arguments + print a._op_two64(2).value + return -2 + except OSError, e: + return -3 + except Exception as e: + print "Got other exception:", e # good + + try: + # causes an OS error and "e" will contain the last debug_event + # in our case exception, and the code is int_divide_zero = 0xC0000094 + print a._op_two64(2, 0, 4).value + return -4 + except OSError, e: + if idaapi.as_uint32(e.args[0].code) != 0xC0000094: + return -5 + except Exception as e: + print "Got other exception:", e + return -6 + a.set_appcall_options(old) + return 1 +# ----------------------------------------------------------------------- +# all the tests that take zero parameters +tests0 = (test_gpa, test_pck_idb_raw, test_pck_bv_raw, + test_unpack_raw, test_pck_idb, test_pck_bv, + test_enum_files, test2, test_exec_throw) +test_log = None # test log file + +# ----------------------------------------------------------------------- +def test_all(): + if test_init() <= 0: + print "test_init() failed!" + return -1 + + # tests 0 + for t in tests0: + print "testing->", t + r = t() + if r <= 0: + return r + # test 1 + for i in xrange(1, st_last): + print "test1 #", i + r = test1(i) + if r <= 0: + return r + + logprint(datetime.date.today().strftime("Python was here: %Y-%m-%d @ %I:%M:%S%p")) + + test_deinit() + + return 1 + +# ----------------------------------------------------------------------- +init() + +# reference to an integer. use i.value to dereference +i = a.byref(5) + +# object representing the str1_t type +o = a.obj(val=5,next=a.obj(val=-2, next=0)) + +# dictionary representing the str1_t type +# (dictionaries will be converted into IDC objects) +d = {'val':5, 'next': {'val':-2, 'next':0} } + +# initialize some pointers +findfirst = a["__imp__FindFirstFileA@8"] +findnext = a["__imp__FindNextFileA@8"] +findclose = a["__imp__FindClose@4"] +getlasterror = a["__imp__GetLastError@0"] +setcurdir = a["__imp__SetCurrentDirectoryA@4"] +beep = a["__imp__Beep@8"] +getwindir = a["__imp__GetWindowsDirectoryA@8"] +getprocaddr = a.proto("__imp__GetProcAddress@8", "int (__stdcall *GetProcAddress)(int hModule, LPCSTR lpProcName);") +getcurdir = a["__imp__GetCurrentDirectoryA@8"] +loadlib = a.proto("__imp__LoadLibraryA@4", "int (__stdcall *LoadLibraryA)(const char *lpLibFileName);") +freelib = a.proto("__imp__FreeLibrary@4", "int (__stdcall *FreeLibrary)(int hLibModule);") +setlasterror = a.typedobj("void __stdcall SetLastError(int dwErrCode);") +getmodulehandlew = a.proto("__imp__GetModuleHandleW@4", "int (__stdcall *GetModuleHandleW)(LPCWSTR lpModuleName);") +virtualalloc = a.typedobj("int __stdcall VirtualAlloc(int lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);") +# typed objects can become calling if an EA was provided. Thus virtualfree.ea = some_address, then we can call virtualfree(., .., ...) +virtualfree = a.typedobj("BOOL __stdcall VirtualFree(int lpAddress, SIZE_T dwSize, DWORD dwFreeType);") +asm1 = a.proto("_asm1", "int __usercall asm1(int a, int b);") +asm2 = a.proto("_asm2", "int __usercall asm2(int a, int b, int c);") +asm3 = a.proto("_asm3", "int __usercall asm3(int f, int a, int b, int c);") +asm4 = a.proto("_asm4", "unsigned __int16 __usercall asm4(unsigned __int8 a, unsigned __int8 b);") +asm5 = a.proto("_asm5", "unsigned int __usercall asm5(unsigned __int8 a, unsigned __int8 b, unsigned __int8 c
, int d);") +altsum = a.proto("_va_altsum", "int __cdecl va_altsum(int n1, ...);") +getcommandline = a.proto("__imp__GetCommandLineA@0", "LPSTR (__stdcall *GetCommandLineA)();") + +# make an appcall with a user defined prototype +subst = a.proto("_subst", "int __pascal subst(int a, int b);") + +# some test identifiers +st_ref1 = 1 +st_ref2 = 2 +st_ref3 = 3 +st_subst = 4 +st_make1 = 5 +st_make2 = 6 +st_asm = 7 +st_ex = 8 +st_byval3 = 9 +st_byvalref1 = 10 +st_altsum = 11 +st_make3 = 12 +st_op64 = 13 +st_ref4 = 14 +st_last = 15 +# some area where we can write some bytes _safely_ +WRITE_AREA = 0x400020 +gr = None +# + +# initialize the test +#test_init() diff --git a/pywraps/py_askusingform.hpp b/pywraps/py_askusingform.hpp new file mode 100644 index 0000000..7729af3 --- /dev/null +++ b/pywraps/py_askusingform.hpp @@ -0,0 +1,355 @@ +#ifndef __PY_ASKUSINGFORM__ +#define __PY_ASKUSINGFORM__ + +// +// + +//--------------------------------------------------------------------------- +// +#define DECLARE_FORM_ACTIONS form_actions_t *fa = (form_actions_t *)p_fa; + +//--------------------------------------------------------------------------- +DECLARE_PY_CLINKED_OBJECT(textctrl_info_t); + +static bool textctrl_info_t_assign(PyObject *self, PyObject *other) +{ + textctrl_info_t *lhs = textctrl_info_t_get_clink(self); + textctrl_info_t *rhs = textctrl_info_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + + *lhs = *rhs; + return true; +} + +//------------------------------------------------------------------------- +static bool textctrl_info_t_set_text(PyObject *self, const char *s) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + if ( ti == NULL ) + return false; + ti->text = s; + return true; +} + +//------------------------------------------------------------------------- +static const char *textctrl_info_t_get_text(PyObject *self) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + return ti == NULL ? "" : ti->text.c_str(); +} + +//------------------------------------------------------------------------- +static bool textctrl_info_t_set_flags(PyObject *self, unsigned int flags) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + if ( ti == NULL ) + return false; + ti->flags = flags; + return true; +} + +//------------------------------------------------------------------------- +static unsigned int textctrl_info_t_get_flags( + PyObject *self, + unsigned int flags) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + return ti == NULL ? 0 : ti->flags; +} + +//------------------------------------------------------------------------- +static bool textctrl_info_t_set_tabsize( + PyObject *self, + unsigned int tabsize) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + if ( ti == NULL ) + return false; + ti->tabsize = tabsize; + return true; +} + +//------------------------------------------------------------------------- +static unsigned int textctrl_info_t_get_tabsize( + PyObject *self, + unsigned int tabsize) +{ + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(self); + return ti == NULL ? 0 : ti->tabsize; +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_enable_field(size_t p_fa, int fid, bool enable) +{ + DECLARE_FORM_ACTIONS; + return fa->enable_field(fid, enable); +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_show_field(size_t p_fa, int fid, bool show) +{ + DECLARE_FORM_ACTIONS; + return fa->show_field(fid, show); +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_move_field( + size_t p_fa, + int fid, + int x, + int y, + int w, + int h) +{ + DECLARE_FORM_ACTIONS; + return fa->move_field(fid, x, y, w, h); +} + +//--------------------------------------------------------------------------- +static int formchgcbfa_get_focused_field(size_t p_fa) +{ + DECLARE_FORM_ACTIONS; + return fa->get_focused_field(); +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_set_focused_field(size_t p_fa, int fid) +{ + DECLARE_FORM_ACTIONS; + return fa->set_focused_field(fid); +} + +//--------------------------------------------------------------------------- +static void formchgcbfa_refresh_field(size_t p_fa, int fid) +{ + DECLARE_FORM_ACTIONS; + return fa->refresh_field(fid); +} + +//--------------------------------------------------------------------------- +static void formchgcbfa_close(size_t p_fa, int fid, int close_normally) +{ + DECLARE_FORM_ACTIONS; + fa->close(close_normally); +} + +//--------------------------------------------------------------------------- +static PyObject *formchgcbfa_get_field_value( + size_t p_fa, + int fid, + int ft, + size_t sz) +{ + DECLARE_FORM_ACTIONS; + switch ( ft ) + { + case 8: + { + // Readonly? Then return the selected index + if ( sz == 1 ) + { + int sel_idx; + if ( fa->get_field_value(fid, &sel_idx) ) + return PyLong_FromLong(sel_idx); + } + // Not readonly? Then return the qstring + else + { + qstring val; + if ( fa->get_field_value(fid, &val) ) + return PyString_FromString(val.c_str()); + } + break; + } + // multilinetext - tuple representing textctrl_info_t + case 7: + { + textctrl_info_t ti; + if ( fa->get_field_value(fid, &ti) ) + return Py_BuildValue("(sII)", ti.text.c_str(), ti.flags, ti.tabsize); + break; + } + // button - uint32 + case 4: + { + uint32 val; + if ( fa->get_field_value(fid, &val) ) + return PyLong_FromUnsignedLong(val); + break; + } + // ushort + case 2: + { + ushort val; + if ( fa->get_field_value(fid, &val) ) + return PyLong_FromUnsignedLong(val); + break; + } + // string label + case 1: + { + char val[MAXSTR]; + if ( fa->get_field_value(fid, val) ) + return PyString_FromString(val); + break; + } + // string input + case 3: + { + qstring val; + val.resize(sz + 1); + if ( fa->get_field_value(fid, val.begin()) ) + return PyString_FromString(val.begin()); + break; + } + case 5: + { + intvec_t intvec; + // Returned as 1-base + if (fa->get_field_value(fid, &intvec)) + { + // Make 0-based + for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) + (*it)--; + + return PyW_IntVecToPyList(intvec); + } + break; + } + // Numeric control + case 6: + { + union + { + sel_t sel; + sval_t sval; + uval_t uval; + ulonglong ull; + } u; + switch ( sz ) + { + case 'S': // sel_t + { + if ( fa->get_field_value(fid, &u.sel) ) + return Py_BuildValue(PY_FMT64, u.sel); + break; + } + // sval_t + case 'n': + case 'N': + case 'D': + case 'O': + case 'Y': + case 'H': + { + if ( fa->get_field_value(fid, &u.sval) ) + return Py_BuildValue(PY_SFMT64, u.sval); + break; + } + case 'L': // uint64 + case 'l': // int64 + { + if ( fa->get_field_value(fid, &u.ull) ) + return Py_BuildValue(sz == 'L' ? "K" : "L", u.ull); + break; + } + case 'M': // uval_t + case '$': // ea_t + { + if ( fa->get_field_value(fid, &u.uval) ) + return Py_BuildValue(PY_FMT64, u.uval); + break; + } + } + break; + } + } + Py_RETURN_NONE; +} + +//--------------------------------------------------------------------------- +static bool formchgcbfa_set_field_value( + size_t p_fa, + int fid, + int ft, + PyObject *py_val) +{ + DECLARE_FORM_ACTIONS; + + switch ( ft ) + { + // dropdown list + case 8: + { + // Editable dropdown list + if ( PyString_Check(py_val) ) + { + qstring val(PyString_AsString(py_val)); + return fa->set_field_value(fid, &val); + } + // Readonly dropdown list + else + { + int sel_idx = PyLong_AsLong(py_val); + return fa->set_field_value(fid, &sel_idx); + } + break; + } + // multilinetext - textctrl_info_t + case 7: + { + textctrl_info_t *ti = (textctrl_info_t *)pyobj_get_clink(py_val); + return ti == NULL ? false : fa->set_field_value(fid, ti); + } + // button - uint32 + case 4: + { + uint32 val = PyLong_AsUnsignedLong(py_val); + return fa->set_field_value(fid, &val); + } + // ushort + case 2: + { + ushort val = PyLong_AsUnsignedLong(py_val) & 0xffff; + return fa->set_field_value(fid, &val); + } + // strings + case 3: + case 1: + return fa->set_field_value(fid, PyString_AsString(py_val)); + // intvec_t + case 5: + { + intvec_t intvec; + // Passed as 0-based + if ( !PyW_PyListToIntVec(py_val, intvec) ) + break; + + // Make 1-based + for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) + (*it)++; + + return fa->set_field_value(fid, &intvec); + } + // Numeric + case 6: + { + uint64 num; + if ( PyW_GetNumber(py_val, &num) ) + return fa->set_field_value(fid, &num); + } + } + return false; +} + +#undef DECLARE_FORM_ACTIONS + +static size_t py_get_AskUsingForm() +{ + return (size_t)AskUsingForm_c; +} + +// + +#endif // __PY_ASKUSINGFORM__ \ No newline at end of file diff --git a/pywraps/py_askusingform.py b/pywraps/py_askusingform.py new file mode 100644 index 0000000..b46e86e --- /dev/null +++ b/pywraps/py_askusingform.py @@ -0,0 +1,1876 @@ +# -------------------------------------------------------------------------- +import os +import gc +import sys + +try: + import _idaapi + from _idaapi import set_script_timeout + import idaapi + from idaapi import py_clinked_object_t + from idaapi import qstrvec_t + stdalone = False +except: + stdalone = True + +print("Standalone: %s" % stdalone) + +pywraps_there = False + +try: + if stdalone: + class object_t(object): pass + class py_clinked_object_t(object): pass + _idaapi = object_t() + _idaapi.choose2_get_embedded = lambda obj: (id(obj), 0) + _idaapi.choose2_get_embedded_selection = lambda obj: None + + _idaapi.textctrl_info_t_set_text = lambda self, v: None + _idaapi.CHOOSER_POPUP_MENU = 1 + pywraps = object_t() + pywraps.py_get_AskUsingForm = lambda: 0 + + class Choose2(object): + CH_MULTI = 1 + def __init__(self, title, cols, flags=0, popup_names=None, + icon=-1, x1=-1, y1=-1, x2=-1, y2=-1, deflt=-1, + embedded=False, width=None, height=None): + pass + + def Close(self): + print("Chooser closing...") + + Embedded = lambda self: 1 + + set_script_timeout = lambda x: x + + else: + import pywraps + pywraps_there = True + from py_choose2 import * + py_clinked_object_t = idaapi.py_clinked_object_t + textctrl_info_t = idaapi.textctrl_info_t + qstrvec_t = idaapi.qstrvec_t + + _idaapi.BADADDR = 0xFFFFFFFF + _idaapi.MAXSTR = 1024 + set_script_timeout = _idaapi.set_script_timeout + + if not hasattr(_idaapi, 'formchgcbfa_enable_field'): + _idaapi.formchgcbfa_enable_field = pywraps.py_formchgcbfa_enable_field + +except Exception as e: + pass +# print("exception: %s" % str(e)) + + +print("Using PyWraps: %s" % pywraps_there) + +# -------------------------------------------------------------------------- +# +#ICON WARNING|QUESTION|INFO|NONE +#AUTOHIDE NONE|DATABASE|REGISTRY|SESSION +#HIDECANCEL +#BUTTON YES|NO|CANCEL "Value|NONE" +#STARTITEM {id:ItemName} +#HELP / ENDHELP +try: + import types + from ctypes import * + # On Windows, we use stdcall + + # Callback for buttons + # typedef void (idaapi *formcb_t)(TView *fields[], int code); + + _FORMCB_T = WINFUNCTYPE(None, c_void_p, c_int) + + # Callback for form change + # typedef int (idaapi *formchgcb_t)(int field_id, form_actions_t &fa); + _FORMCHGCB_T = WINFUNCTYPE(c_int, c_int, c_void_p) +except: + try: + _FORMCB_T = CFUNCTYPE(None, c_void_p, c_int) + _FORMCHGCB_T = CFUNCTYPE(c_int, c_int, c_void_p) + except: + _FORMCHGCB_T = _FORMCB_T = None + + +# ----------------------------------------------------------------------- +# textctrl_info_t clinked object +class textctrl_info_t(py_clinked_object_t): + """Class representing textctrl_info_t""" + + # Some constants + TXTF_AUTOINDENT = 0x0001 + """Auto-indent on new line""" + TXTF_ACCEPTTABS = 0x0002 + """Tab key inserts 'tabsize' spaces""" + TXTF_READONLY = 0x0004 + """Text cannot be edited (but can be selected and copied)""" + TXTF_SELECTED = 0x0008 + """Shows the field with its text selected""" + TXTF_MODIFIED = 0x0010 + """Gets/sets the modified status""" + TXTF_FIXEDFONT = 0x0020 + """The control uses IDA's fixed font""" + + def __init__(self, text="", flags=0, tabsize=0): + py_clinked_object_t.__init__(self) + if text: + self.text = text + if flags: + self.flags = flags + if tabsize: + self.tabsize = tabsize + + def _create_clink(self): + return _idaapi.textctrl_info_t_create() + + def _del_clink(self, lnk): + return _idaapi.textctrl_info_t_destroy(lnk) + + def _get_clink_ptr(self): + return _idaapi.textctrl_info_t_get_clink_ptr(self) + + def assign(self, other): + """Copies the contents of 'other' to 'self'""" + return _idaapi.textctrl_info_t_assign(self, other) + + def __set_text(self, s): + """Sets the text value""" + return _idaapi.textctrl_info_t_set_text(self, s) + + def __get_text(self): + """Sets the text value""" + return _idaapi.textctrl_info_t_get_text(self) + + def __set_flags__(self, flags): + """Sets the flags value""" + return _idaapi.textctrl_info_t_set_flags(self, flags) + + def __get_flags__(self): + """Returns the flags value""" + return _idaapi.textctrl_info_t_get_flags(self) + + def __set_tabsize__(self, tabsize): + """Sets the tabsize value""" + return _idaapi.textctrl_info_t_set_tabsize(self, tabsize) + + def __get_tabsize__(self): + """Returns the tabsize value""" + return _idaapi.textctrl_info_t_get_tabsize(self) + + value = property(__get_text, __set_text) + """Alias for the text property""" + text = property(__get_text, __set_text) + """Text value""" + flags = property(__get_flags__, __set_flags__) + """Flags value""" + tabsize = property(__get_tabsize__, __set_tabsize__) + +# ----------------------------------------------------------------------- +class Form(object): + + FT_ASCII = 'A' + """Ascii string - char *""" + FT_SEG = 'S' + """Segment - sel_t *""" + FT_HEX = 'N' + """Hex number - uval_t *""" + FT_SHEX = 'n' + """Signed hex number - sval_t *""" + FT_COLOR = 'K' + """Color button - bgcolor_t *""" + FT_ADDR = '$' + """Address - ea_t *""" + FT_UINT64 = 'L' + """default base uint64 - uint64""" + FT_INT64 = 'l' + """default base int64 - int64""" + FT_RAWHEX = 'M' + """Hex number, no 0x prefix - uval_t *""" + FT_FILE = 'f' + """File browse - char * at least QMAXPATH""" + FT_DEC = 'D' + """Decimal number - sval_t *""" + FT_OCT = 'O' + """Octal number, C notation - sval_t *""" + FT_BIN = 'Y' + """Binary number, 0b prefix - sval_t *""" + FT_CHAR = 'H' + """Char value -- sval_t *""" + FT_IDENT = 'I' + """Identifier - char * at least MAXNAMELEN""" + FT_BUTTON = 'B' + """Button - def handler(code)""" + FT_DIR = 'F' + """Path to directory - char * at least QMAXPATH""" + FT_TYPE = 'T' + """Type declaration - char * at least MAXSTR""" + _FT_USHORT = '_US' + """Unsigned short""" + FT_FORMCHG = '%/' + """Form change callback - formchgcb_t""" + FT_ECHOOSER = 'E' + """Embedded chooser - idaapi.Choose2""" + FT_MULTI_LINE_TEXT = 't' + """Multi text control - textctrl_info_t""" + FT_DROPDOWN_LIST = 'b' + """Dropdown list control - Form.DropdownControl""" + + FT_CHKGRP = 'C' + FT_CHKGRP2= 'c' + FT_RADGRP = 'R' + FT_RADGRP2= 'r' + + @staticmethod + def fieldtype_to_ctype(tp, i64 = False): + """ + Factory method returning a ctype class corresponding to the field type string + """ + if tp in (Form.FT_SEG, Form.FT_HEX, Form.FT_RAWHEX, Form.FT_ADDR): + return c_ulonglong if i64 else c_ulong + elif tp in (Form.FT_SHEX, Form.FT_DEC, Form.FT_OCT, Form.FT_BIN, Form.FT_CHAR): + return c_longlong if i64 else c_long + elif tp == Form.FT_UINT64: + return c_ulonglong + elif tp == Form.FT_INT64: + return c_longlong + elif tp == Form.FT_COLOR: + return c_ulong + elif tp == Form._FT_USHORT: + return c_ushort + elif tp in (Form.FT_FORMCHG, Form.FT_ECHOOSER): + return c_void_p + else: + return None + + + # + # Generic argument helper classes + # + class NumericArgument(object): + """ + Argument representing various integer arguments (ushort, uint32, uint64, etc...) + @param tp: One of Form.FT_XXX + """ + DefI64 = False + def __init__(self, tp, value): + cls = Form.fieldtype_to_ctype(tp, self.DefI64) + if cls is None: + raise TypeError("Invalid numeric field type: %s" % tp) + # Get a pointer type to the ctype type + self.arg = pointer(cls(value)) + + def __set_value(self, v): + self.arg.contents.value = v + value = property(lambda self: self.arg.contents.value, __set_value) + + + class StringArgument(object): + """ + Argument representing a character buffer + """ + def __init__(self, size=None, value=None): + if size is None: + raise SyntaxError("The string size must be passed") + + if value is None: + self.arg = create_string_buffer(size) + else: + self.arg = create_string_buffer(value, size) + self.size = size + + def __set_value(self, v): + self.arg.value = v + value = property(lambda self: self.arg.value, __set_value) + + + # + # Base control class + # + class Control(object): + def __init__(self): + self.id = 0 + """Automatically assigned control ID""" + + self.arg = None + """Control argument value. This could be one element or a list/tuple (for multiple args per control)""" + + self.form = None + """Reference to the parent form. It is filled by Form.Add()""" + + + def get_tag(self): + """ + Control tag character. One of Form.FT_XXXX. + The form class will expand the {} notation and replace them with the tags + """ + pass + + def get_arg(self): + """ + Control returns the parameter to be pushed on the stack + (Of AskUsingForm()) + """ + return self.arg + + def free(self): + """ + Free the control + """ + # Release the parent form reference + self.form = None + + + # + # Label controls + # + class LabelControl(Control): + """ + Base class for static label control + """ + def __init__(self, tp): + Form.Control.__init__(self) + self.tp = tp + + def get_tag(self): + return '%%%d%s' % (self.id, self.tp) + + + class StringLabel(LabelControl): + """ + String label control + """ + def __init__(self, value, tp=None, sz=1024): + """ + Type field can be one of: + A - ascii string + T - type declaration + I - ident + F - folder + f - file + X - command + """ + if tp is None: + tp = Form.FT_ASCII + Form.LabelControl.__init__(self, tp) + self.size = sz + self.arg = create_string_buffer(value, sz) + + + class NumericLabel(LabelControl, NumericArgument): + """ + Numeric label control + """ + def __init__(self, value, tp=None): + if tp is None: + tp = Form.FT_HEX + Form.LabelControl.__init__(self, tp) + Form.NumericArgument.__init__(self, tp, value) + + + # + # Group controls + # + class GroupItemControl(Control): + """ + Base class for group control items + """ + def __init__(self, tag, parent): + Form.Control.__init__(self) + self.tag = tag + self.parent = parent + # Item position (filled when form is compiled) + self.pos = 0 + + def assign_pos(self): + self.pos = self.parent.next_child_pos() + + def get_tag(self): + return "%s%d" % (self.tag, self.id) + + + class ChkGroupItemControl(GroupItemControl): + """ + Checkbox group item control + """ + def __init__(self, tag, parent): + Form.GroupItemControl.__init__(self, tag, parent) + + def __get_value(self): + return (self.parent.value & (1 << self.pos)) != 0 + + def __set_value(self, v): + pv = self.parent.value + if v: + pv = pv | (1 << self.pos) + else: + pv = pv & ~(1 << self.pos) + + self.parent.value = pv + + checked = property(__get_value, __set_value) + """Get/Sets checkbox item check status""" + + + class RadGroupItemControl(GroupItemControl): + """ + Radiobox group item control + """ + def __init__(self, tag, parent): + Form.GroupItemControl.__init__(self, tag, parent) + + def __get_value(self): + return self.parent.value == self.pos + + def __set_value(self, v): + self.parent.value = self.pos + + selected = property(__get_value, __set_value) + """Get/Sets radiobox item selection status""" + + + class GroupControl(Control, NumericArgument): + """ + Base class for group controls + """ + def __init__(self, children_names, tag, value=0): + Form.Control.__init__(self) + self.children_names = children_names + self.tag = tag + self._reset() + Form.NumericArgument.__init__(self, Form._FT_USHORT, value) + + def _reset(self): + self.childpos = 0 + + def next_child_pos(self): + v = self.childpos + self.childpos += 1 + return v + + def get_tag(self): + return "%d" % self.id + + + class ChkGroupControl(GroupControl): + """ + Checkbox group control class. + It holds a set of checkbox controls + """ + ItemClass = None + """ + Group control item factory class instance + We need this because later we won't be treating ChkGroupControl or RadGroupControl + individually, instead we will be working with GroupControl in general. + """ + def __init__(self, children_names, value=0, secondary=False): + # Assign group item factory class + if Form.ChkGroupControl.ItemClass is None: + Form.ChkGroupControl.ItemClass = Form.ChkGroupItemControl + + Form.GroupControl.__init__( + self, + children_names, + Form.FT_CHKGRP2 if secondary else Form.FT_CHKGRP, + value) + + + class RadGroupControl(GroupControl): + """ + Radiobox group control class. + It holds a set of radiobox controls + """ + ItemClass = None + def __init__(self, children_names, value=0, secondary=False): + """ + Creates a radiogroup control. + @param children_names: A tuple containing group item names + @param value: Initial selected radio item + @param secondory: Allows rendering one the same line as the previous group control. + Use this if you have another group control on the same line. + """ + # Assign group item factory class + if Form.RadGroupControl.ItemClass is None: + Form.RadGroupControl.ItemClass = Form.RadGroupItemControl + + Form.GroupControl.__init__( + self, + children_names, + Form.FT_RADGRP2 if secondary else Form.FT_RADGRP, + value) + + + # + # Input controls + # + class InputControl(Control): + """ + Generic form input control. + It could be numeric control, string control, directory/file browsing, etc... + """ + def __init__(self, tp, width, swidth, hlp = None): + """ + @param width: Display width + @param swidth: String width + """ + Form.Control.__init__(self) + self.tp = tp + self.width = width + self.switdh = swidth + self.hlp = hlp + + def get_tag(self): + return "%s%d:%s:%s:%s" % ( + self.tp, self.id, + self.width, + self.switdh, + ":" if self.hlp is None else self.hlp) + + + class NumericInput(InputControl, NumericArgument): + """ + A composite class serving as a base numeric input control class + """ + def __init__(self, tp=None, value=0, width=50, swidth=10, hlp=None): + if tp is None: + tp = Form.FT_HEX + Form.InputControl.__init__(self, tp, width, swidth, hlp) + Form.NumericArgument.__init__(self, self.tp, value) + + + class ColorInput(NumericInput): + """ + Color button input control + """ + def __init__(self, value = 0): + """ + @param value: Initial color value in RGB + """ + Form.NumericInput.__init__(self, tp=Form.FT_COLOR, value=value) + + + class StringInput(InputControl, StringArgument): + """ + Base string input control class. + This class also constructs a StringArgument + """ + def __init__(self, + tp=None, + width=1024, + swidth=40, + hlp=None, + value=None, + size=None): + """ + @param width: String size. But in some cases it has special meaning. For example in FileInput control. + If you want to define the string buffer size then pass the 'size' argument + @param swidth: Control width + @param value: Initial value + @param size: String size + """ + if tp is None: + tp = Form.FT_ASCII + if not size: + size = width + Form.InputControl.__init__(self, tp, width, swidth, hlp) + Form.StringArgument.__init__(self, size=size, value=value) + + + class FileInput(StringInput): + """ + File Open/Save input control + """ + def __init__(self, + width=512, + swidth=80, + save=False, open=False, + hlp=None, value=None): + + if save == open: + raise ValueError("Invalid mode. Choose either open or save") + if width < 512: + raise ValueError("Invalid width. Must be greater than 512.") + + # The width field is overloaded in this control and is used + # to denote the type of the FileInput dialog (save or load) + # On the other hand it is passed as is to the StringArgument part + Form.StringInput.__init__( + self, + tp=Form.FT_FILE, + width="1" if save else "0", + swidth=swidth, + hlp=hlp, + size=width, + value=value) + + + class DirInput(StringInput): + """ + Directory browsing control + """ + def __init__(self, + width=512, + swidth=80, + hlp=None, + value=None): + + if width < 512: + raise ValueError("Invalid width. Must be greater than 512.") + + Form.StringInput.__init__( + self, + tp=Form.FT_DIR, + width=width, + swidth=swidth, + hlp=hlp, + size=width, + value=value) + + + class ButtonInput(InputControl): + """ + Button control. + A handler along with a 'code' (numeric value) can be associated with the button. + This way one handler can handle many buttons based on the button code (or in other terms id or tag) + """ + def __init__(self, handler, code="", swidth="", hlp=None): + """ + @param handler: Button handler. A callback taking one argument which is the code. + @param code: A code associated with the button and that is later passed to the handler. + """ + Form.InputControl.__init__( + self, + Form.FT_BUTTON, + code, + swidth, + hlp) + self.arg = _FORMCB_T(lambda view, code, h=handler: h(code)) + + + class FormChangeCb(Control): + """ + Form change handler. + This can be thought of like a dialog procedure. + Everytime a form action occurs, this handler will be called along with the control id. + The programmer can then call various form actions accordingly: + - EnableField + - ShowField + - MoveField + - GetFieldValue + - etc... + + Special control IDs: -1 (The form is initialized) and -2 (Ok has been clicked) + + """ + def __init__(self, handler): + """ + Constructs the handler. + @param handler: The handler (preferrably a member function of a class derived from the Form class). + """ + Form.Control.__init__(self) + + # Save the handler + self.handler = handler + + # Create a callback stub + # We use this mechanism to create an intermediate step + # where we can create an 'fa' adapter for use by Python + self.arg = _FORMCHGCB_T(self.helper_cb) + + def helper_cb(self, fid, p_fa): + # Remember the pointer to the forms_action in the parent form + self.form.p_fa = p_fa + + # Call user's handler + r = self.handler(fid) + return 0 if r is None else r + + def get_tag(self): + return Form.FT_FORMCHG + + def free(self): + Form.Control.free(self) + # Remove reference to the handler + # (Normally the handler is a member function in the parent form) + self.handler = None + + + class EmbeddedChooserControl(InputControl): + """ + Embedded chooser control. + This control links to a Chooser2 control created with the 'embedded=True' + """ + def __init__(self, + chooser=None, + swidth=40, + hlp=None): + """ + Embedded chooser control + + @param chooser: A chooser2 instance (must be constructed with 'embedded=True') + """ + + # !! Make sure a chooser instance is passed !! + if chooser is None or not isinstance(chooser, Choose2): + raise ValueError("Invalid chooser passed.") + + # Create an embedded chooser structure from the Choose2 instance + if chooser.Embedded() != 1: + raise ValueError("Failed to create embedded chooser instance.") + + # Construct input control + Form.InputControl.__init__(self, Form.FT_ECHOOSER, "", swidth) + + # Get a pointer to the chooser_info_t and the selection vector + # (These two parameters are the needed arguments for the AskUsingForm()) + emb, sel = _idaapi.choose2_get_embedded(chooser) + + # Get a pointer to a c_void_p constructed from an address + p_embedded = pointer(c_void_p.from_address(emb)) + p_sel = pointer(c_void_p.from_address(sel)) + + # - Create the embedded chooser info on control creation + # - Do not free the embeded chooser because after we get the args + # via Compile() the user can still call Execute() which relies + # on the already computed args + self.arg = (p_embedded, p_sel) + + # Save chooser instance + self.chooser = chooser + + # Add a bogus 'size' attribute + self.size = 0 + + + value = property(lambda self: self.chooser) + """Returns the embedded chooser instance""" + + + def AddCommand(self, + caption, + flags = _idaapi.CHOOSER_POPUP_MENU, + menu_index = -1, + icon = -1): + """ + Adds a new embedded 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 + """ + if not self.form.title: + raise ValueError("Form title is not set!") + + # Encode all information for the AddCommand() in the 'caption' parameter + caption = "%s:%d:%s" % (self.form.title, self.id, caption) + return self.chooser.AddCommand(caption, flags=flags, menu_index=menu_index, icon=icon, emb=2002) + + + def free(self): + """ + Frees the embedded chooser data + """ + self.chooser.Close() + self.chooser = None + Form.Control.free(self) + + + class DropdownListControl(InputControl, qstrvec_t): + """ + Dropdown control + This control allows manipulating a dropdown control + """ + def __init__(self, items=[], readonly=True, selval=0, width=50, swidth=50, hlp = None): + """ + @param items: A string list of items used to prepopulate the control + @param readonly: Specifies whether the dropdown list is editable or not + @param selval: The preselected item index (when readonly) or text value (when editable) + @param width: the control width (n/a if the dropdown list is readonly) + @param swidth: string width + """ + + # Ignore the width if readonly was set + if readonly: + width = 0 + + # Init the input control base class + Form.InputControl.__init__( + self, + Form.FT_DROPDOWN_LIST, + width, + swidth, + hlp) + + # Init the associated qstrvec + qstrvec_t.__init__(self, items) + + # Remember if readonly or not + self.readonly = readonly + + if readonly: + # Create a C integer and remember it + self.__selval = c_int(selval) + val_addr = addressof(self.__selval) + else: + # Create an strvec with one qstring + self.__selval = qstrvec_t([selval]) + # Get address of the first element + val_addr = self.__selval.addressof(0) + + # Two arguments: + # - argument #1: a pointer to the qstrvec containing the items + # - argument #2: an integer to hold the selection + # or + # #2: a qstring to hold the dropdown text control value + self.arg = ( + pointer(c_void_p.from_address(self.clink_ptr)), + pointer(c_void_p.from_address(val_addr)) + ) + + + def __set_selval(self, val): + if self.readonly: + self.__selval.value = val + else: + self.__selval[0] = val + + def __get_selval(self): + # Return the selection index + # or the entered text value + return self.__selval.value if self.readonly else self.__selval[0] + + value = property(__get_selval, __set_selval) + selval = property(__get_selval, __set_selval) + """ + Read/write the selection value. + The value is used as an item index in readonly mode or text value in editable mode + This value can be used only after the form has been closed. + """ + + def free(self): + self._free() + + + def set_items(self, items): + """Sets the dropdown list items""" + self.from_list(items) + + + class MultiLineTextControl(InputControl, textctrl_info_t): + """ + Multi line text control. + This class inherits from textctrl_info_t. Thus the attributes are also inherited + This control allows manipulating a multilinetext control + """ + def __init__(self, text="", flags=0, tabsize=0, width=50, swidth=50, hlp = None): + """ + @param text: Initial text value + @param flags: One of textctrl_info_t.TXTF_.... values + @param tabsize: Tab size + @param width: Display width + @param swidth: String width + """ + # Init the input control base class + Form.InputControl.__init__(self, Form.FT_MULTI_LINE_TEXT, width, swidth, hlp) + + # Init the associated textctrl_info base class + textctrl_info_t.__init__(self, text=text, flags=flags, tabsize=tabsize) + + # Get the argument as a pointer from the embedded ti + self.arg = pointer(c_void_p.from_address(self.clink_ptr)) + + + def free(self): + self._free() + + + # + # Form class + # + def __init__(self, form, controls): + """ + Contruct a Form class. + This class wraps around AskUsingForm() and provides an easier / alternative syntax for describing forms. + The form control names are wrapped inside the opening and closing curly braces and the control themselves are + defined and instantiated via various form controls (subclasses of Form). + + @param form: The form string + @param controls: A dictionary containing the control name as a _key_ and control object as _value_ + """ + self._reset() + self.form = form + """Form string""" + self.controls = controls + """Dictionary of controls""" + self.__args = None + + self.title = None + """The Form title. It will be filled when the form is compiled""" + + + def Free(self): + """ + Frees all resources associated with a compiled form. + Make sure you call this function when you finish using the form. + """ + + # Free all the controls + for ctrl in self.__controls.values(): + ctrl.free() + + # Reset the controls + # (Note that we are not removing the form control attributes, no need) + self._reset() + + + def _reset(self): + """ + Resets the Form class state variables + """ + self.__controls = {} + self.__ctrl_id = 1 + + + def __getitem__(self, name): + """Returns a control object by name""" + return self.__controls[name] + + + def Add(self, name, ctrl, mkattr = True): + """ + Low level function. Prefer AddControls() to this function. + This function adds one control to the form. + + @param name: Control name + @param ctrl: Control object + @param mkattr: Create control name / control object as a form attribute + """ + # Assign a unique ID + ctrl.id = self.__ctrl_id + self.__ctrl_id += 1 + + # Create attribute with control name + if mkattr: + setattr(self, name, ctrl) + + # Remember the control + self.__controls[name] = ctrl + + # Link the form to the control via its form attribute + ctrl.form = self + + # Is it a group? Add each child + if isinstance(ctrl, Form.GroupControl): + self._AddGroup(ctrl, mkattr) + + + def FindControlById(self, id): + """ + Finds a control instance given its id + """ + for ctrl in self.__controls.values(): + if ctrl.id == id: + return ctrl + return None + + + @staticmethod + def _ParseFormTitle(form): + """ + Parses the form's title from the form text + """ + help_state = 0 + for i, line in enumerate(form.split("\n")): + if line.startswith("STARTITEM ") or line.startswith("BUTTON "): + continue + # Skip "HELP" and remember state + elif help_state == 0 and line == "HELP": + help_state = 1 # Mark inside HELP + continue + elif help_state == 1 and line == "ENDHELP": + help_state = 2 # Mark end of HELP + continue + return line.strip() + + return None + + + def _AddGroup(self, Group, mkattr=True): + """ + Internal function. + This function expands the group item names and creates individual group item controls + + @param Group: The group class (checkbox or radio group class) + """ + + # Create group item controls for each child + for child_name in Group.children_names: + self.Add( + child_name, + # Use the class factory + Group.ItemClass(Group.tag, Group), + mkattr) + + + def AddControls(self, controls, mkattr=True): + """ + Adds controls from a dictionary. + The dictionary key is the control name and the value is a Form.Control object + @param controls: The control dictionary + """ + for name, ctrl in controls.items(): + # Add the control + self.Add(name, ctrl, mkattr) + + + def CompileEx(self, form): + """ + Low level function. + Compiles (parses the form syntax and adds the control) the form string and + returns the argument list to be passed the argument list to AskUsingForm(). + + The form controls are wrapped inside curly braces: {ControlName}. + + A special operator can be used to return the ID of a given control by its name: {id:ControlName}. + This is useful when you use the STARTITEM form keyword to set the initially focused control. + + @param form: Compiles the form and returns the arguments needed to be passed to AskUsingForm() + """ + # First argument is the form string + args = [None] + ctrlcnt = 1 + + # Reset all group control internal flags + for ctrl in self.__controls.values(): + if isinstance(ctrl, Form.GroupControl): + ctrl._reset() + + p = 0 + while True: + i1 = form.find("{", p) + # No more items? + if i1 == -1: + break + + # Check if escaped + if (i1 != 0) and form[i1-1] == "\\": + # Remove escape sequence and restart search + form = form[:i1-1] + form[i1:] + + # Skip current marker + p = i1 + + # Continue search + continue + + i2 = form.find("}", i1) + if i2 == -1: + raise SyntaxError("No matching closing brace '}'") + + # Parse control name + ctrlname = form[i1+1:i2] + if not ctrlname: + raise ValueError("Control %d has an invalid name!" % ctrlcnt) + + # Is it the IDOF operator? + if ctrlname.startswith("id:"): + idfunc = True + # Take actual ctrlname + ctrlname = ctrlname[3:] + else: + idfunc = False + + # Find the control + ctrl = self.__controls.get(ctrlname, None) + if ctrl is None: + raise ValueError("No matching control '%s'" % ctrlname) + + # Replace control name by tag + if idfunc: + tag = str(ctrl.id) + else: + tag = ctrl.get_tag() + taglen = len(tag) + form = form[:i1] + tag + form[i2+1:] + + # Set new position + p = i1 + taglen + + # Was it an IDOF() ? No need to push parameters + # Just ID substitution is fine + if idfunc: + continue + + + # For GroupItem controls, there are no individual arguments + # The argument is assigned for the group itself + if isinstance(ctrl, Form.GroupItemControl): + # GroupItem controls will have their position dynamically set + ctrl.assign_pos() + else: + # Push argument(s) + # (Some controls need more than one argument) + arg = ctrl.get_arg() + if isinstance(arg, (types.ListType, types.TupleType)): + # Push all args + args.extend(arg) + else: + # Push one arg + args.append(arg) + + ctrlcnt += 1 + + # Patch in the final form string + args[0] = form + + self.title = self._ParseFormTitle(form) + return args + + + def Compile(self): + """ + Compiles a form and returns the form object (self) and the argument list. + The form object will contain object names corresponding to the form elements + + @return: It will raise an exception on failure. Otherwise the return value is ignored + """ + + # Reset controls + self._reset() + + # Insert controls + self.AddControls(self.controls) + + # Compile form and get args + self.__args = self.CompileEx(self.form) + + return (self, self.__args) + + + def Compiled(self): + """ + Checks if the form has already been compiled + + @return: Boolean + """ + return self.__args is not None + + + def Execute(self): + """ + Displays a compiled form. + @return: 1 - ok ; 0 - cancel + """ + if not self.Compiled(): + raise SyntaxError("Form is not compiled") + + # Call AskUsingForm() + return AskUsingForm(*self.__args) + + + def EnableField(self, ctrl, enable): + """ + Enable or disable an input field + @return: False - no such control + """ + return _idaapi.formchgcbfa_enable_field(self.p_fa, ctrl.id, enable) + + + def ShowField(self, ctrl, show): + """ + Show or hide an input field + @return: False - no such control + """ + return _idaapi.formchgcbfa_show_field(self.p_fa, ctrl.id, show) + + + def MoveField(self, ctrl, x, y, w, h): + """ + Move/resize an input field + + @return: False - no such fiel + """ + return _idaapi.formchgcbfa_move_field(self.p_fa, ctrl.id, x, y, w, h) + + + def GetFocusedField(self): + """ + Get currently focused input field. + @return: None if no field is selected otherwise the control ID + """ + id = _idaapi.formchgcbfa_get_focused_field(self.p_fa) + return self.FindControlById(id) + + + def SetFocusedField(self, ctrl): + """ + Set currently focused input field + @return: False - no such control + """ + return _idaapi.formchgcbfa_set_focused_field(self.p_fa, ctrl.id) + + + def RefreshField(self, ctrl): + """ + Refresh a field + @return: False - no such control + """ + return _idaapi.formchgcbfa_refresh_field(self.p_fa, ctrl.id) + + + def Close(self, close_normally): + """ + Close the form + @param close_normally: + 1: form is closed normally as if the user pressed Enter + 0: form is closed abnormally as if the user pressed Esc + @return: None + """ + return _idaapi.formchgcbfa_close(self.p_fa, close_normally) + + + def GetControlValue(self, ctrl): + """ + Returns the control's value depending on its type + @param ctrl: Form control instance + @return: + - color button, radio controls: integer + - file/dir input, string input and string label: string + - embedded chooser control (0-based indices of selected items): integer list + - for multilinetext control: textctrl_info_t + - dropdown list controls: string (when editable) or index (when readonly) + - None: on failure + """ + tid, sz = self.ControlToFieldTypeIdAndSize(ctrl) + r = _idaapi.formchgcbfa_get_field_value( + self.p_fa, + ctrl.id, + tid, + sz) + # Multilinetext? Unpack the tuple into a new textctrl_info_t instance + if r is not None and tid == 7: + return textctrl_info_t(text=r[0], flags=r[1], tabsize=r[2]) + else: + return r + + + def SetControlValue(self, ctrl, value): + """ + Set the control's value depending on its type + @param ctrl: Form control instance + @param value: + - embedded chooser: a 0-base indices list to select embedded chooser items + - multilinetext: a textctrl_info_t + - dropdown list: an integer designating the selection index if readonly + a string designating the edit control value if not readonly + @return: Boolean true on success + """ + tid, _ = self.ControlToFieldTypeIdAndSize(ctrl) + return _idaapi.formchgcbfa_set_field_value( + self.p_fa, + ctrl.id, + tid, + value) + + + @staticmethod + def ControlToFieldTypeIdAndSize(ctrl): + """ + Converts a control object to a tuple containing the field id + and the associated buffer size + """ + # Input control depend on the associated buffer size (supplied by the user) + + # Make sure you check instances types taking into account inheritance + if isinstance(ctrl, Form.DropdownListControl): + return (8, 1 if ctrl.readonly else 0) + elif isinstance(ctrl, Form.MultiLineTextControl): + return (7, 0) + elif isinstance(ctrl, Form.EmbeddedChooserControl): + return (5, 0) + # Group items or controls + elif isinstance(ctrl, (Form.GroupItemControl, Form.GroupControl)): + return (2, 0) + elif isinstance(ctrl, Form.StringLabel): + return (3, min(_idaapi.MAXSTR, ctrl.size)) + elif isinstance(ctrl, Form.ColorInput): + return (4, 0) + elif isinstance(ctrl, Form.NumericInput): + # Pass the numeric control type + return (6, ord(ctrl.tp[0])) + elif isinstance(ctrl, Form.InputControl): + return (1, ctrl.size) + else: + raise NotImplementedError, "Not yet implemented" + +# -------------------------------------------------------------------------- +# Instantiate AskUsingForm function pointer +try: + import ctypes + # Setup the numeric argument size + Form.NumericArgument.DefI64 = _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL + AskUsingForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_AskUsingForm()) +except: + def AskUsingForm__(*args): + warning("AskUsingForm() needs ctypes library in order to work") + return 0 + + +def AskUsingForm(*args): + """ + Calls the AskUsingForm() + @param: Compiled Arguments obtain through the Form.Compile() function + @return: 1 = ok, 0 = cancel + """ + old = set_script_timeout(0) + r = AskUsingForm__(*args) + set_script_timeout(old) + return r + + +# + +# +# -------------------------------------------------------------------------- +class TestEmbeddedChooserClass(Choose2): + """ + A simple chooser to be used as an embedded chooser + """ + def __init__(self, title, nb = 5, flags=0): + Choose2.__init__(self, + title, + [ ["Address", 10], ["Name", 30] ], + embedded=True, width=30, height=20, flags=flags) + self.n = 0 + self.items = [ self.make_item() for x in xrange(0, nb+1) ] + self.icon = 5 + self.selcount = 0 + + def make_item(self): + r = [str(self.n), "func_%04d" % self.n] + self.n += 1 + return r + + def OnClose(self): + pass + + def OnGetLine(self, n): + print("getline %d" % n) + return self.items[n] + + def OnGetSize(self): + n = len(self.items) + print("getsize -> %d" % n) + return n + +# -------------------------------------------------------------------------- +class MyForm(Form): + def __init__(self): + self.invert = False + self.EChooser = TestEmbeddedChooserClass("E1", flags=Choose2.CH_MULTI) + Form.__init__(self, r"""STARTITEM {id:rNormal} +BUTTON YES* Yeah +BUTTON NO Nope +BUTTON CANCEL Nevermind +Form Test + +{FormChangeCb} +This is a string: +{cStr1}+ +This is an address: +{cAddr1}+ + +Escape\{control} +This is a string: '{cStr2}' +This is a number: {cVal1} + +<#Hint1#Enter name:{iStr1}> +<#Hint2#Select color:{iColor1}> +Browse test +<#Select a file to open#Browse to open:{iFileOpen}> +<#Select a file to save#Browse to save:{iFileSave}> +<#Select dir#Browse for dir:{iDir}> +Type +<#Select type#Write a type:{iType}> +Numbers +<##Enter a selector value:{iSegment}> +<##Enter a raw hex:{iRawHex}> +<##Enter a character:{iChar}> +<##Enter an address:{iAddr}> +Button test +<##Button1:{iButton1}> <##Button2:{iButton2}> + +Check boxes: + + +{cGroup1}> + +Radio boxes: + + +{cGroup2}> + +The end! +""", { + 'cStr1': Form.StringLabel("Hello"), + 'cStr2': Form.StringLabel("StringTest"), + 'cAddr1': Form.NumericLabel(0x401000, Form.FT_ADDR), + 'cVal1' : Form.NumericLabel(99, Form.FT_HEX), + 'iStr1': Form.StringInput(), + 'iColor1': Form.ColorInput(), + 'iFileOpen': Form.FileInput(open=True), + 'iFileSave': Form.FileInput(save=True), + 'iDir': Form.DirInput(), + 'iType': Form.StringInput(tp=Form.FT_TYPE), + 'iSegment': Form.NumericInput(tp=Form.FT_SEG), + 'iRawHex': Form.NumericInput(tp=Form.FT_RAWHEX), + 'iAddr': Form.NumericInput(tp=Form.FT_ADDR), + 'iChar': Form.NumericInput(tp=Form.FT_CHAR), + 'iButton1': Form.ButtonInput(self.OnButton1), + 'iButton2': Form.ButtonInput(self.OnButton2), + 'cGroup1': Form.ChkGroupControl(("rNormal", "rError", "rWarnings")), + 'cGroup2': Form.RadGroupControl(("rRed", "rGreen", "rBlue")), + 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), + 'cEChooser' : Form.EmbeddedChooserControl(self.EChooser) + }) + + + def OnButton1(self, code=0): + print("Button1 pressed") + + + def OnButton2(self, code=0): + print("Button2 pressed") + + + def OnFormChange(self, fid): + if fid == self.iButton1.id: + print("Button1 fchg;inv=%s" % self.invert) + self.SetFocusedField(self.rNormal) + self.EnableField(self.rError, self.invert) + self.invert = not self.invert + elif fid == self.iButton2.id: + g1 = self.GetControlValue(self.cGroup1) + g2 = self.GetControlValue(self.cGroup2) + d = self.GetControlValue(self.iDir) + f = self.GetControlValue(self.iFileOpen) + print("cGroup2:%x;Dir=%s;fopen=%s;cGroup1:%x" % (g1, d, f, g2)) + elif fid == self.cEChooser.id: + l = self.GetControlValue(self.cEChooser) + print("Chooser: %s" % l) + else: + print(">>fid:%d" % fid) + return 1 + + + +# -------------------------------------------------------------------------- +def stdalone_main(): + f = MyForm() + f, args = f.Compile() + print args[0] + print args[1:] + f.rNormal.checked = True + f.rWarnings.checked = True + print hex(f.cGroup1.value) + + f.rGreen.selected = True + print f.cGroup2.value + print "Title: '%s'" % f.title + + f.Free() + +# -------------------------------------------------------------------------- +def ida_main(): + # Create form + global f + f = MyForm() + + # Compile (in order to populate the controls) + f.Compile() + + f.iColor1.value = 0x5bffff + f.iDir.value = os.getcwd() + f.rNormal.checked = True + f.rWarnings.checked = True + f.rGreen.selected = True + f.iStr1.value = "Hello" + f.iFileSave.value = "*.*" + f.iFileOpen.value = "*.*" + # Execute the form + ok = f.Execute() + print("r=%d" % ok) + if ok == 1: + print("f.str1=%s" % f.iStr1.value) + print("f.color1=%x" % f.iColor1.value) + print("f.openfile=%s" % f.iFileOpen.value) + print("f.savefile=%s" % f.iFileSave.value) + print("f.dir=%s" % f.iDir.value) + print("f.type=%s" % f.iType.value) + print("f.seg=%s" % f.iSegment.value) + print("f.rawhex=%x" % f.iRawHex.value) + print("f.char=%x" % f.iChar.value) + print("f.addr=%x" % f.iAddr.value) + print("f.cGroup1=%x" % f.cGroup1.value) + print("f.cGroup2=%x" % f.cGroup2.value) + + sel = f.EChooser.GetEmbSelection() + if sel is None: + print("No selection") + else: + print("Selection: %s" % sel) + + # Dispose the form + f.Free() + +# -------------------------------------------------------------------------- +def ida_main_legacy(): + # Here we simply show how to use the old style form format using Python + + # Sample form from kernwin.hpp + s = """Sample dialog box + + +This is sample dialog box for %A +using address %$ + +<~E~nter value:N:32:16::> +""" + + # Use either StringArgument or NumericArgument to pass values to the function + num = Form.NumericArgument('N', value=123) + ok = idaapi.AskUsingForm(s, + Form.StringArgument("PyAskUsingForm").arg, + Form.NumericArgument('$', 0x401000).arg, + num.arg) + if ok == 1: + print("You entered: %x" % num.value) + +# -------------------------------------------------------------------------- +def test_multilinetext_legacy(): + # Here we text the multi line text control in legacy mode + + # Sample form from kernwin.hpp + s = """Sample dialog box + +This is sample dialog box + +""" + # Use either StringArgument or NumericArgument to pass values to the function + ti = textctrl_info_t("Some initial value") + ok = idaapi.AskUsingForm(s, pointer(c_void_p.from_address(ti.clink_ptr))) + if ok == 1: + print("You entered: %s" % ti.text) + + del ti + +# -------------------------------------------------------------------------- +class MyForm2(Form): + """Simple Form to test multilinetext and combo box controls""" + def __init__(self): + Form.__init__(self, r"""STARTITEM 0 +BUTTON YES* Yeah +BUTTON NO Nope +BUTTON CANCEL NONE +Form Test + +{FormChangeCb} + +""", { + 'txtMultiLineText': Form.MultiLineTextControl(text="Hello"), + 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), + }) + + + def OnFormChange(self, fid): + if fid == self.txtMultiLineText.id: + pass + elif fid == -2: + ti = self.GetControlValue(self.txtMultiLineText) + print "ti.text = %s" % ti.text + else: + print(">>fid:%d" % fid) + return 1 + +# -------------------------------------------------------------------------- +def test_multilinetext(execute=True): + """Test the multilinetext and combobox controls""" + f = MyForm2() + f, args = f.Compile() + if execute: + ok = f.Execute() + else: + print args[0] + print args[1:] + ok = 0 + + if ok == 1: + assert f.txtMultiLineText.text == f.txtMultiLineText.value + print f.txtMultiLineText.text + + f.Free() + +# -------------------------------------------------------------------------- +class MyForm3(Form): + """Simple Form to test multilinetext and combo box controls""" + def __init__(self): + self.__n = 0 + Form.__init__(self, +r"""BUTTON YES* Yeah +BUTTON NO Nope +BUTTON CANCEL NONE +Dropdown list test + +{FormChangeCb} + + +""", { + 'FormChangeCb': Form.FormChangeCb(self.OnFormChange), + 'cbReadonly': Form.DropdownListControl( + items=["red", "green", "blue"], + readonly=True, + selval=1), + 'cbEditable': Form.DropdownListControl( + items=["1MB", "2MB", "3MB", "4MB"], + readonly=False, + selval="4MB"), + 'iButtonAddelement': Form.ButtonInput(self.OnButtonNop), + 'iButtonSetIndex': Form.ButtonInput(self.OnButtonNop), + 'iButtonSetString': Form.ButtonInput(self.OnButtonNop), + }) + + + def OnButtonNop(self, code=0): + """Do nothing, we will handle events in the form callback""" + pass + + def OnFormChange(self, fid): + if fid == self.iButtonSetString.id: + s = idc.AskStr("none", "Enter value") + if s: + self.SetControlValue(self.cbEditable, s) + elif fid == self.iButtonSetIndex.id: + s = idc.AskStr("1", "Enter index value:") + if s: + try: + i = int(s) + except: + i = 0 + self.SetControlValue(self.cbReadonly, i) + elif fid == self.iButtonAddelement.id: + # add a value to the string list + self.__n += 1 + self.cbReadonly.add("some text #%d" % self.__n) + # Refresh the control + self.RefreshField(self.cbReadonly) + elif fid == -2: + s = self.GetControlValue(self.cbEditable) + print "user entered: %s" % s + sel_idx = self.GetControlValue(self.cbReadonly) + + return 1 + +# -------------------------------------------------------------------------- +def test_dropdown(execute=True): + """Test the combobox controls""" + f = MyForm3() + f, args = f.Compile() + if execute: + ok = f.Execute() + else: + print args[0] + print args[1:] + ok = 0 + + if ok == 1: + print "Editable: %s" % f.cbEditable.value + print "Readonly: %s" % f.cbReadonly.value + + f.Free() + +# +# -------------------------------------------------------------------------- + +# +# -------------------------------------------------------------------------- +class MainChooserClass(Choose2): + def __init__(self, title, icon): + Choose2.__init__(self, + title, + [ ["Item", 10] ], + icon=icon, + flags=Choose2.CH_NOIDB, + embedded=True, width=30, height=20) + + def OnClose(self): + pass + + def OnGetLine(self, n): + return ["Option %d" % n] + + def OnGetSize(self): + return 10 + + def OnCommand(self, n, cmd_id): + if cmd_id == self.cmd_id1: + print("Context menu on: %d" % n) + + return 0 + + +# -------------------------------------------------------------------------- +class AuxChooserClass(Choose2): + def __init__(self, title, icon): + Choose2.__init__(self, + title, + [ ["Item", 10] ], + icon=icon, + flags=Choose2.CH_NOIDB | Choose2.CH_MULTI, + embedded=True, width=30, height=20) + + def OnClose(self): + pass + + def OnGetLine(self, n): + return ["Item %d" % n] + + def OnGetSize(self): + t = self.form.main_current_index + return 0 if t < 0 else t+1 + + +# -------------------------------------------------------------------------- +class MyChooserForm(Form): + + # Custom icon data + icon_data = ( + "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52" + "\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1F\xF3\xFF" + "\x61\x00\x00\x00\x7D\x49\x44\x41\x54\x78\xDA\x63\x64\xC0\x0E\xFE" + "\xE3\x10\x67\x24\x28\x00\xD2\xFC\xF3\xAF\x36\x56\xDD\xEC\xCC\x57" + "\x31\xF4\x20\x73\xC0\xB6\xE2\xD2\x8C\x66\x08\x5C\x2F\x8A\x01\x84" + "\x34\x63\x73\x09\x23\xA9\x9A\xD1\x0D\x61\x44\xD7\xCC\xCF\x02\x71" + "\xE2\xC7\x3F\xA8\x06\x62\x13\x07\x19\x42\x7D\x03\x48\xF5\xC6\x20" + "\x34\x00\xE4\x57\x74\xFF\xE3\x92\x83\x19\xC0\x40\x8C\x21\xD8\x34" + "\x33\x40\xA3\x91\x01\x97\x21\xC8\x00\x9B\x66\x38\x01\x33\x00\x44" + "\x50\x92\x94\xB1\xBA\x04\x8B\x66\x9C\x99\x09\xC5\x10\x1C\xE2\x18" + "\xEA\x01\xA3\x65\x55\x0B\x33\x14\x07\x63\x00\x00\x00\x00\x49\x45" + "\x4E\x44\xAE\x42\x60\x82") + + + def Free(self): + # Call the base + Form.Free(self) + + # Free icon + if self.icon_id != 0: + idaapi.free_custom_icon(self.icon_id) + self.icon_id = 0 + + + def __init__(self): + # Load custom icon + self.icon_id = idaapi.load_custom_icon(data=MyChooserForm.icon_data) + if self.icon_id == 0: + raise RuntimeError("Failed to load icon data!") + + self.main_current_index = -1 + self.EChMain = MainChooserClass("MainChooser", self.icon_id) + self.EChAux = AuxChooserClass("AuxChooser", self.icon_id) + + # Link the form to the EChooser + self.EChMain.form = self + self.EChAux.form = self + + Form.__init__(self, r"""STARTITEM 0 +Form with choosers + + {FormChangeCb} + Select an item in the main chooser: + +
+ + + + +""", { + 'ctrlSelectionEdit' : Form.StringInput(), + 'FormChangeCb' : Form.FormChangeCb(self.OnFormChange), + 'ctrlMainChooser' : Form.EmbeddedChooserControl(self.EChMain), + 'ctrlAuxChooser' : Form.EmbeddedChooserControl(self.EChAux), + }) + + + def refresh_selection_edit(self): + if self.main_current_index < 0: + s = "No selection in the main chooser" + else: + s = "Main %d" % self.main_current_index + + # Get selection in the aux chooser + sel = self.GetControlValue(self.ctrlAuxChooser) + if sel: + s = "%s - Aux item(s): %s" % (s, ",".join(str(x) for x in sel)) + + # Update string input + self.SetControlValue(self.ctrlSelectionEdit, s) + + + def OnFormChange(self, fid): + if fid == -1: + print("Initialization") + self.refresh_selection_edit() + + # Add an item to the context menu of the main chooser + id = self.ctrlMainChooser.AddCommand("Test", icon=self.icon_id) + print "id=%d" % id + if id < 0: + print("Failed to install menu for main embedded chooser") + else: + self.EChMain.cmd_id1 = id + + elif fid == -2: + print("Terminating"); + + elif fid == self.ctrlMainChooser.id: + print("main chooser selection change"); + l = self.GetControlValue(self.ctrlMainChooser); + if not l: + self.main_current_index = -1 + else: + self.main_current_index = l[0] + + # Refresh auxiliar chooser + self.RefreshField(self.ctrlAuxChooser) + self.refresh_selection_edit() + + elif fid == self.ctrlAuxChooser.id: + self.refresh_selection_edit() + + elif fid == self.ctrlSelectionEdit.id: + pass + else: + print("unknown id %d" % fid) + + return 1 + +# + +# -------------------------------------------------------------------------- +def main_formchooser(): + global f + f = MyChooserForm() + try: + f.Compile() + r = f.Execute() + print("Execute returned: %d" % r) + f.Free() + except Exception as e: + print("Failed to show form: %s" % str(e)) + +# -------------------------------------------------------------------------- +if __name__=='__main__': + #stdalone_main() if stdalone else main_formchooser() + #stdalone_main() if stdalone else test_multilinetext() + test_dropdown() + #test_multilinetext(False) + diff --git a/pywraps/py_bytes.hpp b/pywraps/py_bytes.hpp new file mode 100644 index 0000000..7ed3473 --- /dev/null +++ b/pywraps/py_bytes.hpp @@ -0,0 +1,218 @@ +#ifndef __PY_IDA_BYTES__ +#define __PY_IDA_BYTES__ + +// +//------------------------------------------------------------------------ +static bool idaapi py_testf_cb(flags_t flags, void *ud) +{ + PyObject *py_flags = PyLong_FromUnsignedLong(flags); + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallFunctionObjArgs((PyObject *) ud, py_flags, NULL); + PYW_GIL_RELEASE; + bool ret = result != NULL && PyObject_IsTrue(result); + Py_XDECREF(result); + Py_XDECREF(py_flags); + return ret; +} + +//------------------------------------------------------------------------ +// 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) ) + return BADADDR; + else + return (next ? nextthat : prevthat)(ea, bound, py_testf_cb, py_callable); +} + +//--------------------------------------------------------------------------- +static int idaapi py_visit_patched_bytes_cb( + ea_t ea, + int32 fpos, + uint32 o, + uint32 v, + void *ud) +{ + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallFunction( + (PyObject *)ud, + PY_FMT64 "iII", + pyul_t(ea), + fpos, + o, + v); + PYW_GIL_RELEASE; + + PyW_ShowCbErr("visit_patched_bytes"); + int ret = (py_result != NULL && PyInt_Check(py_result)) ? PyInt_AsLong(py_result) : 0; + Py_XDECREF(py_result); + return ret; +} +// +//------------------------------------------------------------------------ + +// + +//------------------------------------------------------------------------ +/* +# +def visit_patched_bytes(ea1, ea2, callable): + """ + Enumerates patched bytes in the given range and invokes a callable + @param ea1: start address + @param ea2: end address + @param callable: a Python callable with the following prototype: + callable(ea, fpos, org_val, patch_val). + If the callable returns non-zero then that value will be + returned to the caller and the enumeration will be + interrupted. + @return: Zero if the enumeration was successful or the return + value of the callback if enumeration was interrupted. + """ + pass +# +*/ +static int py_visit_patched_bytes(ea_t ea1, ea_t ea2, PyObject *py_callable) +{ + if ( !PyCallable_Check(py_callable) ) + return 0; + else + return visit_patched_bytes(ea1, ea2, py_visit_patched_bytes_cb, py_callable); +} + +//------------------------------------------------------------------------ +/* +# +def nextthat(ea, maxea, callable): + """ + Find next address with a flag satisfying the function 'testf'. + Start searching from address 'ea'+1 and inspect bytes up to 'maxea'. + maxea is not included in the search range. + + @param callable: a Python callable with the following prototype: + callable(flags). Return True to stop enumeration. + @return: the found address or BADADDR. + """ + pass +# +*/ +static ea_t py_nextthat(ea_t ea, ea_t maxea, PyObject *callable) +{ + return py_npthat(ea, maxea, callable, true); +} + +//--------------------------------------------------------------------------- +static ea_t py_prevthat(ea_t ea, ea_t minea, PyObject *callable) +{ + return py_npthat(ea, minea, callable, false); +} + +//------------------------------------------------------------------------ +/* +# +def get_many_bytes(ea, size): + """ + 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 + { + if ( size <= 0 ) + break; + + // 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, PyString_AsString(py_buf), size); + + // If failed, dispose the Python string + if ( !ok ) + { + Py_DECREF(py_buf); + + py_buf = Py_None; + Py_INCREF(py_buf); + } + + return py_buf; + } while ( false ); + Py_RETURN_NONE; +} + +//--------------------------------------------------------------------------- +/* +# +def get_ascii_contents2(ea, len, type, flags = ACFOPT_ASCII): + """ + Get contents of ascii string + This function returns the displayed part of the string + It works even if the string has not been created in the database yet. + + @param ea: linear address of the string + @param len: length of the string in bytes (including terminating 0) + @param type: type of the string + @param flags: combination of ACFOPT_... + @return: string contents (not including terminating 0) or None + """ + pass +# +*/ +static PyObject *py_get_ascii_contents2( + ea_t ea, + size_t len, + int32 type, + int flags = ACFOPT_ASCII) +{ + char *buf = (char *)qalloc(len+1); + if ( buf == NULL ) + return NULL; + + size_t used_size; + if ( !get_ascii_contents2(ea, len, type, buf, len+1, &used_size) ) + { + qfree(buf); + Py_RETURN_NONE; + } + if ( type == ASCSTR_C && used_size > 0 && buf[used_size-1] == '\0' ) + used_size--; + PyObject *py_buf = PyString_FromStringAndSize((const char *)buf, used_size); + qfree(buf); + return py_buf; +} +//--------------------------------------------------------------------------- +/* +# +def get_ascii_contents(ea, len, type): + """ + Get contents of ascii string + This function returns the displayed part of the string + It works even if the string has not been created in the database yet. + + @param ea: linear address of the string + @param len: length of the string in bytes (including terminating 0) + @param type: type of the string + @return: string contents (not including terminating 0) or None + """ + pass +# +*/ +static PyObject *py_get_ascii_contents( + ea_t ea, + size_t len, + int32 type) +{ + return py_get_ascii_contents2(ea, len, type); +} +// + +#endif diff --git a/pywraps/py_choose.hpp b/pywraps/py_choose.hpp new file mode 100644 index 0000000..6cecdc1 --- /dev/null +++ b/pywraps/py_choose.hpp @@ -0,0 +1,87 @@ +#ifndef __PY_CHOOSE__ +#define __PY_CHOOSE__ + +//--------------------------------------------------------------------------- +// +//--------------------------------------------------------------------------- +uint32 idaapi choose_sizer(void *self) +{ + PyObject *pyres; + uint32 res; + + PYW_GIL_ENSURE; + pyres = PyObject_CallMethod((PyObject *)self, "sizer", ""); + PYW_GIL_RELEASE; + + res = PyInt_AsLong(pyres); + Py_DECREF(pyres); + return res; +} + +//--------------------------------------------------------------------------- +char *idaapi choose_getl(void *self, uint32 n, char *buf) +{ + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod( + (PyObject *)self, + "getl", + "l", + n); + PYW_GIL_RELEASE; + + const char *res; + if (pyres == NULL || (res = PyString_AsString(pyres)) == NULL ) + qstrncpy(buf, "", MAXSTR); + else + qstrncpy(buf, res, MAXSTR); + + Py_XDECREF(pyres); + return buf; +} + +//--------------------------------------------------------------------------- +void idaapi choose_enter(void *self, uint32 n) +{ + PYW_GIL_ENSURE; + Py_XDECREF(PyObject_CallMethod((PyObject *)self, "enter", "l", n)); + PYW_GIL_RELEASE; +} + +//--------------------------------------------------------------------------- +uint32 choose_choose( + void *self, + int flags, + int x0,int y0, + int x1,int y1, + int width, + int deflt, + int icon) +{ + PyObject *pytitle = PyObject_GetAttrString((PyObject *)self, "title"); + const char *title = pytitle != NULL ? PyString_AsString(pytitle) : "Choose"; + + int r = choose( + flags, + x0, y0, + x1, y1, + self, + width, + choose_sizer, + choose_getl, + title, + icon, + deflt, + NULL, /* del */ + NULL, /* inst */ + NULL, /* update */ + NULL, /* edit */ + choose_enter, + NULL, /* destroy */ + NULL, /* popup_names */ + NULL);/* get_icon */ + Py_XDECREF(pytitle); + return r; +} +// + +#endif // __PY_CHOOSE__ \ No newline at end of file diff --git a/pywraps/py_choose2.hpp b/pywraps/py_choose2.hpp new file mode 100644 index 0000000..aa886a9 --- /dev/null +++ b/pywraps/py_choose2.hpp @@ -0,0 +1,956 @@ +#ifndef __PY_CHOOSE2__ +#define __PY_CHOOSE2__ + +// + +//------------------------------------------------------------------------ +// Some defines +#define POPUP_NAMES_COUNT 4 +#define MAX_CHOOSER_MENU_COMMANDS 20 +#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); + return it == choosers.end() ? NULL : 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, + CHOOSE2_HAVE_SELECT = 0x0200, + CHOOSE2_HAVE_REFRESHED = 0x0400, + }; + // Chooser flags + int flags; + + // Callback flags (to tell which callback exists and which not) + // One of CHOOSE2_HAVE_xxxx + unsigned int cb_flags; + chooser_info_t *embedded; + intvec_t embedded_sel; + + // Menu callback index (in the menu_cbs array) + int menu_cb_idx; + + // Chooser title + qstring title; + + // Column widths + intvec_t widths; + + // Python object link + PyObject *self; + // Chooser columns + qstrvec_t cols; + const char **popup_names; + bool ui_cb_hooked; + + // 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) + MENU_COMMAND_CB(10) MENU_COMMAND_CB(11) + MENU_COMMAND_CB(12) MENU_COMMAND_CB(13) + MENU_COMMAND_CB(14) MENU_COMMAND_CB(15) + MENU_COMMAND_CB(16) MENU_COMMAND_CB(17) + MENU_COMMAND_CB(18) MENU_COMMAND_CB(19) + static chooser_cb_t *menu_cbs[MAX_CHOOSER_MENU_COMMANDS]; + + //------------------------------------------------------------------------ + // Static methods to dispatch to member functions + //------------------------------------------------------------------------ + static int idaapi ui_cb(void *obj, int notification_code, va_list va) + { + // UI callback to handle chooser items with attributes + if ( notification_code != ui_get_chooser_item_attrs ) + return 0; + + // Pass events that belong to our chooser only + void *chooser_obj = va_arg(va, void *); + if ( obj != chooser_obj ) + return 0; + + 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 void idaapi s_select(void *obj, const intvec_t &sel) + { + thisobj->on_select(sel); + } + + static void idaapi s_refreshed(void *obj) + { + thisobj->on_refreshed(); + } + + 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_enter(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(); + } + + //------------------------------------------------------------------------ + // Member functions corresponding to each chooser2() callback + //------------------------------------------------------------------------ + void clear_popup_names() + { + if ( popup_names == NULL ) + return; + + for ( int i=0; i=0; i-- ) + line_arr[i][0] = '\0'; + + // Call Python + PYW_GIL_ENSURE; + PyObject *list = PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1); + PYW_GIL_RELEASE; + if ( list == NULL ) + return; + + // Go over the List returned by Python and convert to C strings + 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() + { + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL); + PYW_GIL_RELEASE; + if ( pyres == NULL ) + return 0; + + size_t res = PyInt_AsLong(pyres); + Py_DECREF(pyres); + return res; + } + + void on_refreshed() + { + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_REFRESHED, NULL); + PYW_GIL_RELEASE; + Py_XDECREF(pyres); + } + + void on_select(const intvec_t &intvec) + { + PYW_GIL_ENSURE; + PyObject *py_list = PyW_IntVecToPyList(intvec); + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_SELECT, "O", py_list); + PYW_GIL_RELEASE; + Py_XDECREF(pyres); + Py_XDECREF(py_list); + } + + void on_close() + { + // Call Python + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL); + PYW_GIL_RELEASE; + Py_XDECREF(pyres); + + // Delete this instance if none modal and not embedded + if ( !is_modal() && get_embedded() == NULL ) + delete this; + } + + int on_delete_line(int lineno) + { + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod( + self, + (char *)S_ON_DELETE_LINE, + "i", + lineno - 1); + PYW_GIL_RELEASE; + + if ( pyres == NULL ) + return lineno; + + size_t res = PyInt_AsLong(pyres); + Py_DECREF(pyres); + return res + 1; + } + + int on_refresh(int lineno) + { + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod( + self, + (char *)S_ON_REFRESH, + "i", + lineno - 1); + PYW_GIL_RELEASE; + if ( pyres == NULL ) + return lineno; + + size_t res = PyInt_AsLong(pyres); + Py_DECREF(pyres); + return res + 1; + } + + void on_insert_line() + { + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL); + PYW_GIL_RELEASE; + Py_XDECREF(pyres); + } + + void on_enter(int lineno) + { + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod( + self, + (char *)S_ON_SELECT_LINE, + "i", + lineno - 1); + PYW_GIL_RELEASE; + Py_XDECREF(pyres); + } + + void on_edit_line(int lineno) + { + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod( + self, + (char *)S_ON_EDIT_LINE, + "i", + lineno - 1); + PYW_GIL_RELEASE; + Py_XDECREF(pyres); + } + + int on_command(int cmd_id, int lineno) + { + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod( + self, + (char *)S_ON_COMMAND, + "ii", + lineno - 1, + cmd_id); + PYW_GIL_RELEASE; + + if ( pyres==NULL ) + return lineno; + + size_t res = PyInt_AsLong(pyres); + Py_XDECREF(pyres); + return res; + } + + int on_get_icon(int lineno) + { + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod( + self, + (char *)S_ON_GET_ICON, + "i", + lineno - 1); + PYW_GIL_RELEASE; + + size_t res = PyInt_AsLong(pyres); + Py_XDECREF(pyres); + return res; + } + + void on_get_line_attr(int lineno, chooser_item_attrs_t *attr) + { + PYW_GIL_ENSURE; + PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1); + PYW_GIL_RELEASE; + + 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), + embedded(NULL), menu_cb_idx(0), + self(NULL), popup_names(NULL), ui_cb_hooked(false) + { + } + + ~py_choose2_t() + { + // Remove from list + choose2_del_instance(self); + + // Uninstall hooks + install_hooks(false); + + delete embedded; + Py_XDECREF(self); + clear_popup_names(); + } + + static py_choose2_t *find_chooser(const char *title) + { + return (py_choose2_t *) get_chooser_obj(title); + } + + void close() + { + // Will trigger on_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 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; + + // For embedded chooser, the "caption" will be overloaded to encode + // the AskUsingForm's title, caption and embedded chooser id + // Title:EmbeddedChooserID:Caption + char title_buf[MAXSTR]; + const char *ptitle; + + // Embedded chooser? + if ( get_embedded() != NULL ) + { + static const char delimiter[] = ":"; + char temp[MAXSTR]; + qstrncpy(temp, caption, sizeof(temp)); + + char *p = strtok(temp, delimiter); + if ( p == NULL ) + return -1; + + // Copy the title + char title_str[MAXSTR]; + qstrncpy(title_str, p, sizeof(title_str)); + + // Copy the echooser ID + p = strtok(NULL, delimiter); + if ( p == NULL ) + return -1; + + char id_str[10]; + qstrncpy(id_str, p, sizeof(id_str)); + + // Form the new title of the form: "AskUsingFormTitle:EchooserId" + qsnprintf(title_buf, sizeof(title_buf), "%s:%s", title_str, id_str); + + // Adjust the title + ptitle = title_buf; + + // Adjust the caption + p = strtok(NULL, delimiter); + caption += (p - temp); + } + else + { + ptitle = title.c_str(); + } + + if ( !add_chooser_command( + ptitle, + caption, + menu_cbs[menu_cb_idx], + menu_index, + icon, + flags)) + { + return -1; + } + + return menu_cb_idx++; + } + + // Create a chooser. + // If it detects the "embedded" attribute, then it will create a chooser_info_t structure + // Otherwise the chooser window is created and displayed + int create(PyObject *self) + { + PyObject *attr; + + // Get flags + attr = PyW_TryGetAttrString(self, S_FLAGS); + if ( attr == NULL ) + return -1; + + flags = PyInt_Check(attr) != 0 ? PyInt_AsLong(attr) : 0; + Py_DECREF(attr); + + // Get the title + if ( !PyW_GetStringAttr(self, S_TITLE, &title) ) + return -1; + + // Get columns + attr = PyW_TryGetAttrString(self, "cols"); + if ( attr == NULL ) + return -1; + + // Get col count + int ncols = int(PyList_Size(attr)); + + // Get cols caption and widthes + cols.qclear(); + for ( int i=0; iself = self; + + // Hook to notification point (to handle chooser item attributes) + install_hooks(true); + + // Check if *embedded + attr = PyW_TryGetAttrString(self, S_EMBEDDED); + if ( attr != NULL && PyObject_IsTrue(attr) == 1 ) + { + // Create an embedded chooser structure + embedded = new chooser_info_t(); + embedded->obj = this; + embedded->cb = sizeof(chooser_info_t); + embedded->title = title.c_str(); + embedded->columns = ncols; + embedded->deflt = deflt; + embedded->flags = flags; + embedded->width = pts[0]; // Take x1 + embedded->height = pts[1]; // Take y1 + embedded->icon = icon; + embedded->popup_names = popup_names; + embedded->widths = widths.begin(); + embedded->destroyer = s_destroy; + embedded->getl = s_getl; + embedded->sizer = s_sizer; + embedded->del = (cb_flags & CHOOSE2_HAVE_DEL) != 0 ? s_del : NULL; + embedded->edit = (cb_flags & CHOOSE2_HAVE_EDIT) != 0 ? s_edit : NULL; + embedded->enter = (cb_flags & CHOOSE2_HAVE_ENTER) != 0 ? s_enter : NULL; + embedded->get_icon = (cb_flags & CHOOSE2_HAVE_GETICON) != 0 ? s_get_icon : NULL; + embedded->ins = (cb_flags & CHOOSE2_HAVE_INS) != 0 ? s_ins : NULL; + embedded->update = (cb_flags & CHOOSE2_HAVE_UPDATE) != 0 ? s_update : NULL; + embedded->get_attrs = NULL; + // Fill callbacks that are only present in idaq + if ( is_idaq() ) + { + embedded->select = (cb_flags & CHOOSE2_HAVE_SELECT) != 0 ? s_select : NULL; + embedded->refresh = (cb_flags & CHOOSE2_HAVE_REFRESHED)!= 0 ? s_refreshed : NULL; + } + else + { + embedded->select = NULL; + embedded->refresh = NULL; + } + } + Py_XDECREF(attr); + + // Create the chooser (if not embedded) + int r; + if ( embedded == NULL ) + { + r = ::choose2( + flags, + pts[0], pts[1], pts[2], pts[3], + this, + ncols, + &widths[0], + s_sizer, + s_getl, + title.c_str(), + icon, + deflt, + (cb_flags & CHOOSE2_HAVE_DEL) != 0 ? s_del : NULL, + (cb_flags & CHOOSE2_HAVE_INS) != 0 ? s_ins : NULL, + (cb_flags & CHOOSE2_HAVE_UPDATE) != 0 ? s_update : NULL, + (cb_flags & CHOOSE2_HAVE_EDIT) != 0 ? s_edit : NULL, + (cb_flags & CHOOSE2_HAVE_ENTER) != 0 ? s_enter : NULL, + s_destroy, + popup_names, + (cb_flags & CHOOSE2_HAVE_GETICON) != 0 ? s_get_icon : NULL); + + clear_popup_names(); + + // Modal chooser return the index of the selected item + if ( is_modal() ) + r--; + } + // Embedded chooser? + else + { + // Return success + r = 1; + } + + return r; + } + + inline PyObject *get_self() + { + return self; + } + + void refresh() + { + refresh_chooser(title.c_str()); + } + + bool is_modal() + { + return (flags & CH_MODAL) != 0; + } + + intvec_t *get_sel_vec() + { + return &embedded_sel; + } + + chooser_info_t *get_embedded() const + { + return embedded; + } +}; + +//------------------------------------------------------------------------ +// Initialize the callback pointers +#define DECL_MENU_COMMAND_CB(id) s_menu_command_##id +chooser_cb_t *py_choose2_t::menu_cbs[MAX_CHOOSER_MENU_COMMANDS] = +{ + DECL_MENU_COMMAND_CB(0), DECL_MENU_COMMAND_CB(1), + DECL_MENU_COMMAND_CB(2), DECL_MENU_COMMAND_CB(3), + DECL_MENU_COMMAND_CB(4), DECL_MENU_COMMAND_CB(5), + DECL_MENU_COMMAND_CB(6), DECL_MENU_COMMAND_CB(7), + DECL_MENU_COMMAND_CB(8), DECL_MENU_COMMAND_CB(9), + DECL_MENU_COMMAND_CB(10), DECL_MENU_COMMAND_CB(11), + DECL_MENU_COMMAND_CB(12), DECL_MENU_COMMAND_CB(13), + DECL_MENU_COMMAND_CB(14), DECL_MENU_COMMAND_CB(15), + DECL_MENU_COMMAND_CB(16), DECL_MENU_COMMAND_CB(17), + DECL_MENU_COMMAND_CB(18), DECL_MENU_COMMAND_CB(19) +}; +#undef DECL_MENU_COMMAND_CB + +#undef POPUP_NAMES_COUNT +#undef MAX_CHOOSER_MENU_COMMANDS +#undef thisobj +#undef thisdecl +#undef MENU_COMMAND_CB + +//------------------------------------------------------------------------ +int choose2_create(PyObject *self, bool embedded) +{ + py_choose2_t *c2; + + c2 = choose2_find_instance(self); + if ( c2 != NULL ) + { + if ( !embedded ) + c2->activate(); + return 1; + } + + c2 = new py_choose2_t(); + + choose2_add_instance(self, c2); + + int r = c2->create(self); + // Non embedded chooser? Return immediately + if ( !embedded ) + return r; + + // Embedded chooser was not created? + if ( c2->get_embedded() == NULL || r != 1 ) + { + delete c2; + r = 0; + } + return r; +} + +//------------------------------------------------------------------------ +void choose2_close(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if ( c2 == NULL ) + return; + + // Modal or embedded chooser? + if ( c2->get_embedded() != NULL || c2->is_modal() ) + { + // Then simply delete the instance + delete c2; + } + else + { + // Close the chooser. + // In turn this will lead to the deletion of the object + 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(); +} + +//------------------------------------------------------------------------ +PyObject *choose2_get_embedded_selection(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + chooser_info_t *embedded; + + if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL ) + Py_RETURN_NONE; + + // Returned as 1-based + intvec_t &intvec = *c2->get_sel_vec(); + + // Make 0-based + for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it) + (*it)--; + + return PyW_IntVecToPyList(intvec); +} + +//------------------------------------------------------------------------ +// Return the C instances as 64bit numbers +PyObject *choose2_get_embedded(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + chooser_info_t *embedded; + + if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL ) + Py_RETURN_NONE; + else + return Py_BuildValue("(KK)", + PY_ULONG_LONG(embedded), + PY_ULONG_LONG(c2->get_sel_vec())); +} + +//------------------------------------------------------------------------ +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); + return c2 == NULL ? NULL : c2->get_self(); +} +// + +//--------------------------------------------------------------------------- +// +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_create(PyObject *self, bool embedded); +void choose2_activate(PyObject *self); +PyObject *choose2_get_embedded(PyObject *self); +PyObject *choose2_get_embedded_selection(PyObject *self); +// + +//--------------------------------------------------------------------------- +// Testing functions. They belong to PyWraps and won't be copied to IDAPython +//--------------------------------------------------------------------------- + +static void NT_CDECL choose2_test_embedded(chooser_info_t *embedded) +{ + msg("cb=%d -> looks %valid\n", + embedded->cb, + embedded->cb == sizeof(chooser_info_t) ? "" : "in"); +} +static size_t choose2_get_test_embedded() +{ + return (size_t)choose2_test_embedded; +} +#endif // __PY_CHOOSE2__ \ No newline at end of file diff --git a/pywraps/py_choose2.py b/pywraps/py_choose2.py new file mode 100644 index 0000000..2b65e60 --- /dev/null +++ b/pywraps/py_choose2.py @@ -0,0 +1,390 @@ +# ----------------------------------------------------------------------- +# Standalone and testing code +from ctypes import * + +try: + import _idaapi +except: + print("Please try me from inside IDA") + sys.exit(0) + +try: + import pywraps + pywraps_there = True + print("Choose2: using pywraps") + + _idaapi.choose2_create = pywraps.py_choose2_create + _idaapi.choose2_activate = pywraps.py_choose2_activate + _idaapi.choose2_refresh = pywraps.py_choose2_refresh + _idaapi.choose2_close = pywraps.py_choose2_close + _idaapi.choose2_add_command = pywraps.py_choose2_add_command + _idaapi.choose2_get_embedded = pywraps.py_choose2_get_embedded + _idaapi.choose2_get_embedded_selection = pywraps.py_choose2_get_embedded_selection + + try: + # Get function address + # void test_embedded(chooser_info_t *) + TEST_EMBEDDED = CFUNCTYPE(c_void_p, c_void_p) + test_embedded = TEST_EMBEDDED(pywraps.py_choose2_get_test_embedded()) + except Exception as e: + test_embedded = None + print("Choose2: Exception: %s" % str(e)) + +except Exception as e: + pywraps_there = False + print("Choose2: Not using pywraps: %s" % str(e)) + +# ----------------------------------------------------------------------- +# +class Choose2(object): + """ + Choose2 wrapper class. + + Some constants are defined in this class. Please refer to kernwin.hpp for more information. + """ + + CH_MODAL = 0x01 + """Modal chooser""" + + CH_MULTI = 0x02 + """Allow multi selection""" + + CH_MULTI_EDIT = 0x04 + CH_NOBTNS = 0x08 + CH_ATTRS = 0x10 + CH_NOIDB = 0x20 + """use the chooser even without an open database, same as x0=-2""" + + 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, + embedded=False, width=None, height=None): + """ + 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 | Choose2.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 or an index to a custom loaded icon) + @param x1, y1, x2, y2: The default location + @param embedded: Create as embedded chooser + @param width: Embedded chooser width + @param height: Embedded chooser height + """ + 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 + self.embedded = embedded + if embedded: + self.x1 = width + self.y1 = height + + + def Embedded(self): + """ + Creates an embedded chooser (as opposed to Show()) + @return: Returns 1 on success + """ + return _idaapi.choose2_create(self, True) + + + def GetEmbSelection(self): + """ + Returns the selection associated with an embedded chooser + + @return: + - None if chooser is not embedded + - A list with selection indices (0-based) + """ + return _idaapi.choose2_get_embedded_selection(self) + + + def Show(self, modal=False): + """ + Activates or creates a chooser window + @param modal: Display as modal dialog + @return: For modal choosers it will return the selected item index (0-based) + """ + if modal: + self.flags |= Choose2.CH_MODAL + + # Disable the timeout + old = _idaapi.set_script_timeout(0) + n = _idaapi.choose2_create(self, False) + _idaapi.set_script_timeout(old) + + # Delete the modal chooser instance + self.Close() + + return n + else: + self.flags &= ~Choose2.CH_MODAL + return _idaapi.choose2_create(self, False) + + + 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, + emb=None): + """ + 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 + """ + + # Use the 'emb' as a sentinel. It will be passed the correct value from the EmbeddedChooserControl + if self.embedded and ((emb is None) or (emb != 2002)): + raise RuntimeError("Please add a command through EmbeddedChooserControl.AddCommand()") + 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. +# @param n: Line number (0-based) +# @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 (0-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 a line is selected and then Ok or double click was pressed +# @param n: Line number (0-based) +# """ +# pass +# +# def OnSelectionChange(self, sel_list): +# """ +# Called when the selection changes +# @param sel_list: A list of selected item indices +# """ +# pass +# +# def OnDeleteLine(self, n): +# """ +# Called when a line is about to be deleted +# @param n: Line number (0-based) +# """ +# return self.n +# +# def OnRefresh(self, n): +# """ +# Triggered when the 'Refresh' is called from the popup menu item. +# +# @param n: The currently selected line (0-based) at the time of the refresh call +# @return: Return the number of elements +# """ +# return self.n +# +# def OnRefreshed(self): +# """ +# Triggered when a refresh happens (for example due to column sorting) +# @param n: Line number (0-based) +# @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) +# @param n: Line number (0-based) +# """ +# return -1 +# +# def OnGetLineAttr(self, n): +# """ +# Return list [bgcolor, flags=CHITEM_XXXX] or None; check chooser_item_attrs_t +# @param n: Line number (0-based) +# """ +# return [0x0, CHITEM_BOLD] +# +# + +# ----------------------------------------------------------------------- +class MyChoose2(Choose2): + + def __init__(self, title, nb = 5, flags=0, width=None, height=None, embedded=False, modal=False): + Choose2.__init__( + self, + title, + [ ["Address", 10], ["Name", 30] ], + flags = flags, + width = width, + height = height, + embedded = embedded) + self.n = 0 + self.items = [ self.make_item() for x in xrange(0, nb+1) ] + self.icon = 5 + self.selcount = 0 + self.modal = modal + self.popup_names = ["Inzert", "Del leet", "Ehdeet", "Ree frech"] + + print("created %s" % str(self)) + + def OnClose(self): + print "closed", str(self) + + def OnEditLine(self, n): + self.items[n][1] = self.items[n][1] + "*" + print("editing %d" % n) + + def OnInsertLine(self): + self.items.append(self.make_item()) + print("insert line") + + def OnSelectLine(self, n): + self.selcount += 1 + Warning("[%02d] selectline '%s'" % (self.selcount, n)) + + def OnGetLine(self, n): + print("getline %d" % n) + return self.items[n] + + def OnGetSize(self): + n = len(self.items) + print("getsize -> %d" % n) + return n + + def OnDeleteLine(self, n): + print("del %d " % n) + del self.items[n] + return n + + def OnRefresh(self, n): + print("refresh %d" % n) + return n + + def OnCommand(self, n, cmd_id): + if cmd_id == self.cmd_a: + print "command A selected @", n + elif cmd_id == self.cmd_b: + print "command B selected @", n + else: + print "Unknown command:", cmd_id, "@", n + return 1 + + def OnGetIcon(self, n): + r = self.items[n] + t = self.icon + r[1].count("*") + print "geticon", n, t + return t + + def show(self): + t = self.Show(self.modal) + if t < 0: + return False + if not self.modal: + self.cmd_a = self.AddCommand("command A") + self.cmd_b = self.AddCommand("command B") + print("Show() returned: %d\n" % t) + return True + + def make_item(self): + r = [str(self.n), "func_%04d" % self.n] + self.n += 1 + return r + + def OnGetLineAttr(self, n): + print("getlineattr %d" % n) + if n == 1: + return [0xFF0000, 0] + + +# ----------------------------------------------------------------------- +def test_choose2(modal=False): + global c + c = MyChoose2("Choose2 - sample 1", nb=10, modal=modal) + r = c.show() + +# ----------------------------------------------------------------------- +def test_choose2_embedded(): + global c + c = MyChoose2("Choose2 - embedded", nb=12, embedded = True, width=123, height=222) + r = c.Embedded() + if r == 1: + try: + if test_embedded: + o, sel = _idaapi.choose2_get_embedded(c) + print("o=%s, type(o)=%s" % (str(o), type(o))) + test_embedded(o) + finally: + c.Close() + +# ----------------------------------------------------------------------- +if __name__ == '__main__': + #test_choose2_embedded() + test_choose2(False) \ No newline at end of file diff --git a/pywraps/py_cli.hpp b/pywraps/py_cli.hpp new file mode 100644 index 0000000..1250d33 --- /dev/null +++ b/pywraps/py_cli.hpp @@ -0,0 +1,293 @@ +#ifndef __PYWRAPS_CLI__ +#define __PYWRAPS_CLI__ + +// +//-------------------------------------------------------------------------- +#define MAX_PY_CLI 12 + +// Callbacks table +// This structure was devised because the cli callbacks have no user-data parameter +struct py_cli_cbs_t +{ + bool (idaapi *execute_line)(const char *line); + bool (idaapi *complete_line)( + qstring *completion, + const char *prefix, + int n, + const char *line, + int x); + bool (idaapi *keydown)( + qstring *line, + int *p_x, + int *p_sellen, + int *vk_key, + int shift); +}; + +// CLI Python wrapper class +class py_cli_t +{ +private: + //-------------------------------------------------------------------------- + cli_t cli; + PyObject *self; + qstring cli_sname, cli_lname, cli_hint; + + //-------------------------------------------------------------------------- + static py_cli_t *py_clis[MAX_PY_CLI]; + static const py_cli_cbs_t py_cli_cbs[MAX_PY_CLI]; + //-------------------------------------------------------------------------- +#define IMPL_PY_CLI_CB(CBN) \ + static bool idaapi s_keydown##CBN(qstring *line, int *p_x, int *p_sellen, int *vk_key, int shift) \ + { \ + return py_clis[CBN]->on_keydown(line, p_x, p_sellen, vk_key, shift); \ + } \ + static bool idaapi s_execute_line##CBN(const char *line) \ + { \ + return py_clis[CBN]->on_execute_line(line); \ + } \ + static bool idaapi s_complete_line##CBN(qstring *completion, const char *prefix, int n, const char *line, int x) \ + { \ + return py_clis[CBN]->on_complete_line(completion, prefix, n, line, x); \ + } + + IMPL_PY_CLI_CB(0); IMPL_PY_CLI_CB(1); IMPL_PY_CLI_CB(2); IMPL_PY_CLI_CB(3); + IMPL_PY_CLI_CB(4); IMPL_PY_CLI_CB(5); IMPL_PY_CLI_CB(6); IMPL_PY_CLI_CB(7); + IMPL_PY_CLI_CB(8); IMPL_PY_CLI_CB(9); IMPL_PY_CLI_CB(10); IMPL_PY_CLI_CB(11); +#undef IMPL_PY_CLI_CB + + //-------------------------------------------------------------------------- + // callback: the user pressed Enter + // CLI is free to execute the line immediately or ask for more lines + // Returns: true-executed line, false-ask for more lines + bool on_execute_line(const char *line) + { + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallMethod( + self, + (char *)S_ON_EXECUTE_LINE, + "s", + line); + PYW_GIL_RELEASE; + + bool ok = result != NULL && PyObject_IsTrue(result); + PyW_ShowCbErr(S_ON_EXECUTE_LINE); + Py_XDECREF(result); + return ok; + } + + //-------------------------------------------------------------------------- + // callback: a keyboard key has been pressed + // This is a generic callback and the CLI is free to do whatever + // it wants. + // line - current input line (in/out argument) + // p_x - pointer to current x coordinate of the cursor (in/out) + // p_sellen - pointer to current selection length (usually 0) + // p_vk_key - pointer to virtual key code (in/out) + // if the key has been handled, it should be reset to 0 by CLI + // shift - shift state + // Returns: true-modified input line or x coordinate or selection length + // This callback is optional + bool on_keydown( + qstring *line, + int *p_x, + int *p_sellen, + int *vk_key, + int shift) + { + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallMethod( + self, + (char *)S_ON_KEYDOWN, + "siiHi", + line->c_str(), + *p_x, + *p_sellen, + *vk_key, + shift); + PYW_GIL_RELEASE; + + bool ok = result != NULL && PyTuple_Check(result); + + PyW_ShowCbErr(S_ON_KEYDOWN); + + if ( ok ) + { + Py_ssize_t sz = PyTuple_Size(result); + PyObject *item; + + if ( sz > 0 && (item = PyTuple_GetItem(result, 0)) != NULL && PyString_Check(item) ) + *line = PyString_AsString(item); + + if ( sz > 1 && (item = PyTuple_GetItem(result, 1)) != NULL && PyInt_Check(item) ) + *p_x = PyInt_AsLong(item); + + if ( sz > 2 && (item = PyTuple_GetItem(result, 2)) != NULL && PyInt_Check(item) ) + *p_sellen = PyInt_AsLong(item); + + if ( sz > 3 && (item = PyTuple_GetItem(result, 3)) != NULL && PyInt_Check(item) ) + *vk_key = PyInt_AsLong(item) & 0xffff; + } + + Py_XDECREF(result); + return ok; + } + + // callback: the user pressed Tab + // Find a completion number N for prefix PREFIX + // LINE is given as context information. X is the index where PREFIX starts in LINE + // New prefix should be stored in PREFIX. + // Returns: true if generated a new completion + // This callback is optional + bool on_complete_line( + qstring *completion, + const char *prefix, + int n, + const char *line, + int x) + { + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallMethod( + self, + (char *)S_ON_COMPLETE_LINE, + "sisi", + prefix, + n, + line, + x); + PYW_GIL_RELEASE; + + bool ok = result != NULL && PyString_Check(result); + PyW_ShowCbErr(S_ON_COMPLETE_LINE); + if ( ok ) + *completion = PyString_AsString(result); + + Py_XDECREF(result); + return ok; + } + + // Private ctor (use bind()) + py_cli_t() + { + } + +public: + //--------------------------------------------------------------------------- + static int bind(PyObject *py_obj) + { + int cli_idx; + // Find an empty slot + for ( cli_idx = 0; cli_idx < MAX_PY_CLI; ++cli_idx ) + { + if ( py_clis[cli_idx] == NULL ) + break; + } + py_cli_t *py_cli = NULL; + do + { + // No free slots? + if ( cli_idx >= MAX_PY_CLI ) + break; + + // Create a new instance + py_cli = new py_cli_t(); + PyObject *attr; + + // Start populating the 'cli' member + py_cli->cli.size = sizeof(cli_t); + + // Store 'flags' + if ( (attr = PyW_TryGetAttrString(py_obj, S_FLAGS)) == NULL ) + { + py_cli->cli.flags = 0; + } + else + { + py_cli->cli.flags = PyLong_AsLong(attr); + Py_DECREF(attr); + } + + // Store 'sname' + if ( !PyW_GetStringAttr(py_obj, "sname", &py_cli->cli_sname) ) + break; + py_cli->cli.sname = py_cli->cli_sname.c_str(); + + // Store 'lname' + if ( !PyW_GetStringAttr(py_obj, "lname", &py_cli->cli_lname) ) + break; + py_cli->cli.lname = py_cli->cli_lname.c_str(); + + // Store 'hint' + if ( !PyW_GetStringAttr(py_obj, "hint", &py_cli->cli_hint) ) + break; + py_cli->cli.hint = py_cli->cli_hint.c_str(); + + // Store callbacks + if ( !PyObject_HasAttrString(py_obj, S_ON_EXECUTE_LINE) ) + break; + py_cli->cli.execute_line = py_cli_cbs[cli_idx].execute_line; + + py_cli->cli.complete_line = PyObject_HasAttrString(py_obj, S_ON_COMPLETE_LINE) ? py_cli_cbs[cli_idx].complete_line : NULL; + py_cli->cli.keydown = PyObject_HasAttrString(py_obj, S_ON_KEYDOWN) ? py_cli_cbs[cli_idx].keydown : NULL; + + // install CLI + install_command_interpreter(&py_cli->cli); + + // Take reference to this object + py_cli->self = py_obj; + Py_INCREF(py_obj); + + // Save the instance + py_clis[cli_idx] = py_cli; + + return cli_idx; + } while (false); + + delete py_cli; + return -1; + } + + //--------------------------------------------------------------------------- + static void unbind(int cli_idx) + { + // Out of bounds or not set? + if ( cli_idx < 0 || cli_idx >= MAX_PY_CLI || py_clis[cli_idx] == NULL ) + return; + + py_cli_t *py_cli = py_clis[cli_idx]; + remove_command_interpreter(&py_cli->cli); + + Py_DECREF(py_cli->self); + delete py_cli; + + py_clis[cli_idx] = NULL; + + return; + } +}; +py_cli_t *py_cli_t::py_clis[MAX_PY_CLI] = {NULL}; +#define DECL_PY_CLI_CB(CBN) { s_execute_line##CBN, s_complete_line##CBN, s_keydown##CBN } +const py_cli_cbs_t py_cli_t::py_cli_cbs[MAX_PY_CLI] = +{ + DECL_PY_CLI_CB(0), DECL_PY_CLI_CB(1), DECL_PY_CLI_CB(2), DECL_PY_CLI_CB(3), + DECL_PY_CLI_CB(4), DECL_PY_CLI_CB(5), DECL_PY_CLI_CB(6), DECL_PY_CLI_CB(7), + DECL_PY_CLI_CB(8), DECL_PY_CLI_CB(9), DECL_PY_CLI_CB(10), DECL_PY_CLI_CB(11) +}; +#undef DECL_PY_CLI_CB +// + +//-------------------------------------------------------------------------- + +// +static int py_install_command_interpreter(PyObject *py_obj) +{ + return py_cli_t::bind(py_obj); +} + +static void py_remove_command_interpreter(int cli_idx) +{ + py_cli_t::unbind(cli_idx); +} +// +//--------------------------------------------------------------------------- +#endif // __PYWRAPS_CLI__ diff --git a/pywraps/py_cli.py b/pywraps/py_cli.py new file mode 100644 index 0000000..354f13f --- /dev/null +++ b/pywraps/py_cli.py @@ -0,0 +1,187 @@ +# ----------------------------------------------------------------------- +# Standalone and testing code +import sys +try: + import pywraps + pywraps_there = True + print "Using pywraps" +except: + pywraps_there = False + print "Using IDAPython" + +try: + import _idaapi + from idaapi import pyidc_opaque_object_t +except: + print "Please run this script from inside IDA" + sys.exit(0) + +if pywraps_there: + _idaapi.install_command_interpreter = pywraps.install_command_interpreter + _idaapi.remove_command_interpreter = pywraps.remove_command_interpreter + +# ----------------------------------------------------------------------- +# +class cli_t(pyidc_opaque_object_t): + """ + cli_t wrapper class. + + This class allows you to implement your own command line interface handlers. + """ + + def __init__(self): + self.__cli_idx = -1 + self.__clink__ = None + + + def register(self, flags = 0, sname = None, lname = None, hint = None): + """ + Registers the CLI. + + @param flags: Feature bits. No bits are defined yet, must be 0 + @param sname: Short name (displayed on the button) + @param lname: Long name (displayed in the menu) + @param hint: Hint for the input line + + @return Boolean: True-Success, False-Failed + """ + + # Already registered? + if self.__cli_idx >= 0: + return True + + if sname is not None: self.sname = sname + if lname is not None: self.lname = lname + if hint is not None: self.hint = hint + + # Register + self.__cli_idx = _idaapi.install_command_interpreter(self) + return False if self.__cli_idx < 0 else True + + + def unregister(self): + """ + Unregisters the CLI (if it was registered) + """ + if self.__cli_idx < 0: + return False + + _idaapi.remove_command_interpreter(self.__cli_idx) + self.__cli_idx = -1 + return True + + + def __del__(self): + self.unregister() + + # + # Implement these methods in the subclass: + # +# +# def OnExecuteLine(self, line): +# """ +# The user pressed Enter. The CLI is free to execute the line immediately or ask for more lines. +# +# This callback is mandatory. +# +# @param line: typed line(s) +# @return Boolean: True-executed line, False-ask for more lines +# """ +# return True +# +# def OnKeydown(self, line, x, sellen, vkey, shift): +# """ +# A keyboard key has been pressed +# This is a generic callback and the CLI is free to do whatever it wants. +# +# This callback is optional. +# +# @param line: current input line +# @param x: current x coordinate of the cursor +# @param sellen: current selection length (usually 0) +# @param vkey: virtual key code. if the key has been handled, it should be returned as zero +# @param shift: shift state +# +# @return: +# None - Nothing was changed +# tuple(line, x, sellen, vkey): if either of the input line or the x coordinate or the selection length has been modified. +# It is possible to return a tuple with None elements to preserve old values. Example: tuple(new_line, None, None, None) or tuple(new_line) +# """ +# return None +# +# def OnCompleteLine(self, prefix, n, line, prefix_start): +# """ +# The user pressed Tab. Find a completion number N for prefix PREFIX +# +# This callback is optional. +# +# @param prefix: Line prefix at prefix_start (string) +# @param n: completion number (int) +# @param line: the current line (string) +# @param prefix_start: the index where PREFIX starts in LINE (int) +# +# @return: None if no completion could be generated otherwise a String with the completion suggestion +# """ +# return None +# + +# + +# ----------------------------------------------------------------------- +# +class mycli_t(cli_t): + flags = 0 + sname = "pycli" + lname = "Python CLI" + hint = "pycli hint" + + def OnExecuteLine(self, line): + """ + The user pressed Enter. The CLI is free to execute the line immediately or ask for more lines. + + This callback is mandatory. + + @param line: typed line(s) + @return Boolean: True-executed line, False-ask for more lines + """ + print "OnExecute:", line + return True + + def OnKeydown(self, line, x, sellen, vkey, shift): + """ + A keyboard key has been pressed + This is a generic callback and the CLI is free to do whatever it wants. + + This callback is optional. + + @param line: current input line + @param x: current x coordinate of the cursor + @param sellen: current selection length (usually 0) + @param vkey: virtual key code. if the key has been handled, it should be returned as zero + @param shift: shift state + + @return: + None - Nothing was changed + tuple(line, x, sellen, vkey): if either of the input line or the x coordinate or the selection length has been modified. + It is possible to return a tuple with None elements to preserve old values. Example: tuple(new_line, None, None, None) or tuple(new_line) + """ + print "Onkeydown: line=%s x=%d sellen=%d vkey=%d shift=%d" % (line, x, sellen, vkey, shift) + return None + + def OnCompleteLine(self, prefix, n, line, prefix_start): + """ + The user pressed Tab. Find a completion number N for prefix PREFIX + + This callback is optional. + + @param prefix: Line prefix at prefix_start (string) + @param n: completion number (int) + @param line: the current line (string) + @param prefix_start: the index where PREFIX starts in LINE (int) + + @return: None if no completion could be generated otherwise a String with the completion suggestion + """ + print "OnCompleteLine: prefix=%s n=%d line=%s prefix_start=%d" % (prefix, n, line, prefix_start) + return None +# +# ----------------------------------------------------------------------- diff --git a/pywraps/py_custdata.hpp b/pywraps/py_custdata.hpp new file mode 100644 index 0000000..984256c --- /dev/null +++ b/pywraps/py_custdata.hpp @@ -0,0 +1,699 @@ +#ifndef __PY_IDA_CUSTDATA__ +#define __PY_IDA_CUSTDATA__ + +// + +//------------------------------------------------------------------------ +class py_custom_data_type_t +{ + data_type_t dt; + qstring dt_name, dt_menu_name, dt_hotkey, dt_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; + + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod( + _this->py_self, + (char *)S_MAY_CREATE_AT, + PY_FMT64 PY_FMT64, + pyul_t(ea), + pyul_t(nbytes)); + PYW_GIL_RELEASE; + + PyW_ShowCbErr(S_MAY_CREATE_AT); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + // !=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; + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod( + _this->py_self, + (char *)S_CALC_ITEM_SIZE, + PY_FMT64 PY_FMT64, + pyul_t(ea), + pyul_t(maxsize)); + PYW_GIL_RELEASE; + + if ( PyW_ShowCbErr(S_CALC_ITEM_SIZE) || py_result == NULL ) + return 0; + + uint64 num = 0; + PyW_GetNumber(py_result, &num); + Py_XDECREF(py_result); + return asize_t(num); + } + +public: + const char *get_name() const + { + return dt_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 + if ( !PyW_GetStringAttr(py_obj, S_NAME, &dt_name) ) + break; + + dt.name = dt_name.c_str(); + + // menu_name (optional) + if ( PyW_GetStringAttr(py_obj, S_MENU_NAME, &dt_menu_name) ) + dt.menu_name = dt_menu_name.c_str(); + + // asm_keyword (optional) + if ( PyW_GetStringAttr(py_obj, S_ASM_KEYWORD, &dt_asm_keyword) ) + dt.asm_keyword = dt_asm_keyword.c_str(); + + // hotkey (optional) + if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &dt_hotkey) ) + dt.hotkey = dt_hotkey.c_str(); + + // value_size + py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); + if ( py_attr != NULL && PyInt_Check(py_attr) ) + dt.value_size = PyInt_AsLong(py_attr); + Py_XDECREF(py_attr); + + // props + py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); + if ( py_attr != NULL && PyInt_Check(py_attr) ) + dt.props = PyInt_AsLong(py_attr); + Py_XDECREF(py_attr); + + // may_create_at + py_attr = PyW_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 = PyW_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 df_name, df_menu_name, df_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; + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod( + _this->py_self, + (char *)S_PRINTF, + "O" PY_FMT64 "ii", + py_value, + pyul_t(current_ea), + operand_num, + dtid); + PYW_GIL_RELEASE; + // Done with the string + Py_DECREF(py_value); + + // Error while calling the function? + if ( PyW_ShowCbErr(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; + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod( + _this->py_self, + (char *)S_SCAN, + "s" PY_FMT64, + input, + pyul_t(current_ea), + operand_num); + PYW_GIL_RELEASE; + + // Error while calling the function? + if ( PyW_ShowCbErr(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; + + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod( + _this->py_self, + (char *)S_ANALYZE, + PY_FMT64 "i", + pyul_t(current_ea), + operand_num); + PYW_GIL_RELEASE; + + PyW_ShowCbErr(S_ANALYZE); + Py_XDECREF(py_result); + } +public: + py_custom_data_format_t() + { + dfid = -1; + py_self = NULL; + } + + const char *get_name() const + { + return df_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 + if ( !PyW_GetStringAttr(py_obj, S_NAME, &df_name) ) + break; + df.name = df_name.c_str(); + + // menu_name (optional) + if ( PyW_GetStringAttr(py_obj, S_MENU_NAME, &df_menu_name) ) + df.menu_name = df_menu_name.c_str(); + + // props + py_attr = PyW_TryGetAttrString(py_obj, S_PROPS); + if ( py_attr != NULL && PyInt_Check(py_attr) ) + df.props = PyInt_AsLong(py_attr); + Py_XDECREF(py_attr); + + // hotkey + if ( PyW_GetStringAttr(py_obj, S_HOTKEY, &df_hotkey) ) + df.hotkey = df_hotkey.c_str(); + + // value_size + py_attr = PyW_TryGetAttrString(py_obj, S_VALUE_SIZE); + if ( py_attr != NULL && PyInt_Check(py_attr) ) + df.value_size = PyInt_AsLong(py_attr); + Py_XDECREF(py_attr); + + // text_width + py_attr = PyW_TryGetAttrString(py_obj, S_TEXT_WIDTH); + if ( py_attr != NULL && PyInt_Check(py_attr) ) + df.text_width = PyInt_AsLong(py_attr); + Py_XDECREF(py_attr); + + // print cb + py_attr = PyW_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 = PyW_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 = PyW_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; + + // Update the format ID + py_attr = PyInt_FromLong(dfid); + PyObject_SetAttrString(py_obj, S_ID, py_attr); + Py_DECREF(py_attr); + + py_attr = NULL; + } while ( false ); + + 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); +} +// + +//------------------------------------------------------------------------ +// + +//------------------------------------------------------------------------ +/* +# +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); +} + +// + +#endif diff --git a/pywraps/py_custdata.py b/pywraps/py_custdata.py new file mode 100644 index 0000000..f9dee53 --- /dev/null +++ b/pywraps/py_custdata.py @@ -0,0 +1,241 @@ +# ----------------------------------------------------------------------- +# Standalone and testing code +import sys +try: + import pywraps + pywraps_there = True + print "Using pywraps" +except: + pywraps_there = False + print "Not using pywraps" + +try: + import _idaapi +except: + print "Please try me from inside IDA" + sys.exit(0) + +import struct + +if pywraps_there: + _idaapi.register_custom_data_type = pywraps.register_custom_data_type + _idaapi.unregister_custom_data_type = pywraps.unregister_custom_data_type + _idaapi.register_custom_data_format = pywraps.register_custom_data_format + _idaapi.unregister_custom_data_format = pywraps.unregister_custom_data_format + _idaapi.get_custom_data_format = pywraps.get_custom_data_format + _idaapi.get_custom_data_type = pywraps.get_custom_data_type + +# ----------------------------------------------------------------------- +# +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 + +# +# ----------------------------------------------------------------------- diff --git a/pywraps/py_custview.hpp b/pywraps/py_custview.hpp new file mode 100644 index 0000000..5abdd67 --- /dev/null +++ b/pywraps/py_custview.hpp @@ -0,0 +1,1110 @@ +#ifndef __PYWRAPS_CUSTVIEWER__ +#define __PYWRAPS_CUSTVIEWER__ +// +//--------------------------------------------------------------------------- +// 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 *); + if ( (_this->_features & HAVE_HINT) == 0 || place == NULL || _this->_cv != viewer ) + return 0; + else + return _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 | FORM_CLOSE_LATER); + } + + //-------------------------------------------------------------------------- + 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 && !qisspace(ptr[-1]) ) + ptr--; + + // find the end of the word + const char *begin = ptr; + ptr = line + x; + while ( !qisspace(*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); + 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) + { + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift); + PYW_GIL_RELEASE; + PyW_ShowCbErr(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) + { + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift); + PYW_GIL_RELEASE; + PyW_ShowCbErr(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() + { + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL); + PYW_GIL_RELEASE; + PyW_ShowCbErr(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 ) + { + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL); + PYW_GIL_RELEASE; + + PyW_ShowCbErr(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) + { + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod( + py_self, + (char *)S_ON_KEYDOWN, + "ii", + vk_key, + shift); + PYW_GIL_RELEASE; + + PyW_ShowCbErr(S_ON_KEYDOWN); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + //-------------------------------------------------------------------------- +// OnPopupShow + virtual bool on_popup() + { + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod( + py_self, + (char *)S_ON_POPUP, + NULL); + PYW_GIL_RELEASE; + + PyW_ShowCbErr(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); + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod( + py_self, + (char *)S_ON_HINT, + PY_FMT64, + pyul_t(ln)); + PYW_GIL_RELEASE; + + PyW_ShowCbErr(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) + { + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod( + py_self, + (char *)S_ON_POPUP_MENU, + PY_FMT64, + pyul_t(menu_id)); + PYW_GIL_RELEASE; + + PyW_ShowCbErr(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 + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +// +// +// 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 // __PYWRAPS_CUSTVIEWER__ diff --git a/pywraps/py_custview.py b/pywraps/py_custview.py new file mode 100644 index 0000000..948afae --- /dev/null +++ b/pywraps/py_custview.py @@ -0,0 +1,458 @@ +# ----------------------------------------------------------------------- +# Standalone and testing code +import sys, struct + +try: + import _idaapi +except: + print "Please try me from inside IDA" + sys.exit(0) + +try: + import pywraps + pywraps_there = True + print "Using pywraps" + + _idaapi.pyscv_init = pywraps.pyscv_init + _idaapi.pyscv_close = pywraps.pyscv_close + _idaapi.pyscv_add_line = pywraps.pyscv_add_line + _idaapi.pyscv_delete = pywraps.pyscv_delete + _idaapi.pyscv_refresh = pywraps.pyscv_refresh + _idaapi.pyscv_show = pywraps.pyscv_show + _idaapi.pyscv_clear_popup_menu = pywraps.pyscv_clear_popup_menu + _idaapi.pyscv_del_line = pywraps.pyscv_del_line + _idaapi.pyscv_get_pos = pywraps.pyscv_get_pos + _idaapi.pyscv_refresh_current = pywraps.pyscv_refresh_current + _idaapi.pyscv_get_current_line = pywraps.pyscv_get_current_line + _idaapi.pyscv_is_focused = pywraps.pyscv_is_focused + _idaapi.pyscv_add_popup_menu = pywraps.pyscv_add_popup_menu + _idaapi.pyscv_get_line = pywraps.pyscv_get_line + _idaapi.pyscv_jumpto = pywraps.pyscv_jumpto + _idaapi.pyscv_edit_line = pywraps.pyscv_edit_line + _idaapi.pyscv_patch_line = pywraps.pyscv_patch_line + _idaapi.pyscv_insert_line = pywraps.pyscv_insert_line + _idaapi.pyscv_count = pywraps.pyscv_count + _idaapi.pyscv_get_selection = pywraps.pyscv_get_selection + _idaapi.pyscv_clear_lines = pywraps.pyscv_clear_lines + _idaapi.pyscv_get_current_word = pywraps.pyscv_get_current_word +except: + pywraps_there = False + print "Not using pywraps" + +# ----------------------------------------------------------------------- +# +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 or -1 on failure""" + r = self.GetPos(mouse) + return -1 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 +# +# + +# + +# ----------------------------------------------------------------------- +class mycv_t(simplecustviewer_t): + def Create(self, sn=None): + # Form the title + title = "Simple custom view test" + if sn: + title += " %d" % sn + + # Create the customviewer + if not simplecustviewer_t.Create(self, title): + return False + self.menu_hello = self.AddPopupMenu("Hello") + self.menu_world = self.AddPopupMenu("World") + + for i in xrange(0, 100): + self.AddLine("Line %d" % i) + +# self.Jump(0) + + return True + + 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 + """ + word = self.GetCurrentWord() + if not word: word = "" + print "OnDblClick, shift=%d, current word=%s" % (shift, word) + 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 " + self.title + + 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) + # ESCAPE? + if vkey == 27: + self.Close() + # VK_DELETE + elif vkey == 46: + n = self.GetLineNo() + if n is not None: + self.DelLine(n) + self.Refresh() + print "Deleted line %d" % n + # Goto? + elif vkey == ord('G'): + n = self.GetLineNo() + if n is not None: + v = idc.AskLong(self.GetLineNo(), "Where to go?") + if v: + self.Jump(v, 0, 5) + elif vkey == ord('R'): + print "refreshing...." + self.Refresh() + elif vkey == ord('C'): + print "refreshing current line..." + self.RefreshCurrent() + elif vkey == ord('A'): + s = idc.AskStr("NewLine%d" % self.Count(), "Append new line") + self.AddLine(s) + self.Refresh() + elif vkey == ord('X'): + print "Clearing all lines" + self.ClearLines() + self.Refresh() + elif vkey == ord('I'): + n = self.GetLineNo() + s = idc.AskStr("InsertedLine%d" % n, "Insert new line") + self.InsertLine(n, s) + self.Refresh() + elif vkey == ord('E'): + l = self.GetCurrentLine(notags=1) + if not l: + return False + n = self.GetLineNo() + print "curline=<%s>" % l + l = l + idaapi.COLSTR("*", idaapi.SCOLOR_VOIDOP) + self.EditLine(n, l) + self.RefreshCurrent() + print "Edited line %d" % n + else: + return False + return True + + 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 AddPopupMenu() + @return: Boolean + """ + print "OnPopupMenu, menu_id=%d" % menu_id + if menu_id == self.menu_hello: + print "Hello" + elif menu_id == self.menu_world: + print "World" + else: + # Unhandled + return False + return True + +# ----------------------------------------------------------------------- +try: + # created already? + mycv + print "Already created, will close it..." + mycv.Close() + del mycv +except: + pass + +def show_win(): + x = mycv_t() + if not x.Create(): + print "Failed to create!" + return None + x.Show() + return x +mycv = show_win() +if not mycv: + del mycv + +def make_many(n): + L = [] + for i in xrange(1, n+1): + v = mycv_t() + if not v.Create(i): + break + v.Show() + L.append(v) + return L + +# diff --git a/pywraps/py_cvt.hpp b/pywraps/py_cvt.hpp new file mode 100644 index 0000000..6b5b996 --- /dev/null +++ b/pywraps/py_cvt.hpp @@ -0,0 +1,1095 @@ +#ifndef __PYCVT__ +#define __PYCVT__ + +// + +//------------------------------------------------------------------------ +// String constants used +static const char S_PY_IDCCVT_VALUE_ATTR[] = "__idc_cvt_value__"; +static const char S_PY_IDCCVT_ID_ATTR[] = "__idc_cvt_id__"; +static const char S_PY_IDC_OPAQUE_T[] = "py_idc_cvt_helper_t"; +static const char S_PY_IDC_GLOBAL_VAR_FMT[] = "__py_cvt_gvar_%d"; + +// 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 + +//--------------------------------------------------------------------------- +// 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 = PyW_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) && PyW_GetNumber(py_attr, &val.u64) ) + ; // nothing to be done + // A string array? + else if ( (ft == FT_STRARR || ft == FT_NUM16ARR || ft == FT_CHRARR_STATIC ) + && (PyList_CheckExact(py_attr) || PyW_IsSequenceType(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 ( !PyW_GetNumber(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; + } +}; + +//------------------------------------------------------------------------- +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) && !PyW_IsSequenceType(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; i 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; +} + +//------------------------------------------------------------------------ +// 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; +} + +//------------------------------------------------------------------------- +// 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 ( PyW_GetNumberAsIDC(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) || PyW_IsSequenceType(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 = PyW_ObjectToString(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 + PyW_ObjectToString(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 = PyW_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 = PyW_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_by_id(PY_CLSID_CVT_INT64); + if ( py_cls == NULL ) + return CIP_FAILED; + PYW_GIL_ENSURE; + *py_var = PyObject_CallFunctionObjArgs(py_cls, PyLong_FromLongLong(idc_var.i64), NULL); + PYW_GIL_RELEASE; + Py_DECREF(py_cls); + if ( PyW_GetError() || *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(30160); + + *py_var = PyFloat_FromDouble(x); + break; + } + else + return CIP_IMMUTABLE; + + case VT_REF: + { + if ( *py_var == NULL ) + { + PyObject *py_cls = get_idaapi_attr_by_id(PY_CLSID_CVT_BYREF); + if ( py_cls == NULL ) + return CIP_FAILED; + + // Create a byref object with None value. We populate it later + PYW_GIL_ENSURE; + *py_var = PyObject_CallFunctionObjArgs(py_cls, Py_None, NULL); + PYW_GIL_RELEASE; + Py_DECREF(py_cls); + if ( PyW_GetError() || *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 = PyW_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 ) + { + // Get skeleton class reference + PyObject *py_cls = get_idaapi_attr_by_id(PY_CLSID_APPCALL_SKEL_OBJ); + if ( py_cls == NULL ) + return CIP_FAILED; + + // Call constructor + PYW_GIL_ENSURE; + obj = PyObject_CallFunctionObjArgs(py_cls, NULL); + PYW_GIL_RELEASE; + Py_DECREF(py_cls); + if ( PyW_GetError() || 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 (recursively) + 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; +} + +//------------------------------------------------------------------------- +// 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 +bool pyw_convert_idc_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 ) + { + if ( errbuf != 0 && errbufsize > 0 ) + 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; i 0 ) + qsnprintf(errbuf, errbufsize, "arg#%d has wrong type %d", i, args[i].vtype); + return false; + } + + if ( as_tupple ) + { + // Opaque object? + if ( cvt == CIP_OK_NODECREF ) + { + // Increment reference for opaque objects. + // (A tupple will steal references of its set items, + // and for an opaque object we want it to still exist + // even if the tuple is gone) + Py_INCREF(py_obj); + } + // Save argument + PyTuple_SetItem(py_tuple, i, py_obj); + } + else + { + pargs.push_back(py_obj); + // Do not decrement reference of opaque objects + decref->push_back(cvt == CIP_OK); + } + } + return true; +} + +//------------------------------------------------------------------------- +// Frees arguments returned by pyw_convert_idc_args() +void pyw_free_idc_args( + ppyobject_vec_t &pargs, + boolvec_t *decref) +{ + 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(); +} + +// + +#endif diff --git a/pywraps/py_dbg.hpp b/pywraps/py_dbg.hpp new file mode 100644 index 0000000..0e3cff2 --- /dev/null +++ b/pywraps/py_dbg.hpp @@ -0,0 +1,699 @@ +#ifndef __PYDBG__ +#define __PYDBG__ + +// + +//------------------------------------------------------------------------- +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_NOTASK); +} + +//------------------------------------------------------------------------- +static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas) +{ + PyObject *py_list = PyList_New(areas.size()); + meminfo_vec_t::const_iterator it, it_end(areas.end()); + Py_ssize_t i = 0; + for ( it=areas.begin(); it!=it_end; ++it, ++i ) + { + const memory_info_t &mi = *it; + // startEA endEA name sclass sbase bitness perm + PyList_SetItem(py_list, i, + Py_BuildValue("("PY_FMT64 PY_FMT64 "ss" PY_FMT64 "II)", + pyul_t(mi.startEA), + pyul_t(mi.endEA), + mi.name.c_str(), + mi.sclass.c_str(), + pyul_t(mi.sbase), + (unsigned int)(mi.bitness), + (unsigned int)mi.perm)); + } + return py_list; +} + +//------------------------------------------------------------------------- +PyObject *py_appcall( + ea_t func_ea, + thid_t tid, + PyObject *py_type, + PyObject *py_fields, + PyObject *arg_list) +{ + if ( !PyList_Check(arg_list) ) + return NULL; + + const char *type = py_type == Py_None ? NULL : PyString_AS_STRING(py_type); + const char *fields = py_fields == Py_None ? NULL : PyString_AS_STRING(py_fields); + + // Convert Python arguments into IDC values + qvector idc_args; + int sn = 0; + Py_ssize_t nargs = PyList_Size(arg_list); + idc_args.resize(nargs); + bool ok = true; + for ( Py_ssize_t i=0; i%s\n", int(i), s.c_str()); + } + // Convert it + if ( pyvar_to_idcvar(py_item, &idc_args[i], &sn) < CIP_OK ) + { + ok = false; + break; + } + } + + // Set exception message + if ( !ok ) + { + PyErr_SetString( + PyExc_ValueError, + "PyAppCall: Failed to convert Python values to IDC values"); + return NULL; + } + + if ( (debug & IDA_DEBUG_APPCALL) != 0 ) + { + msg("input variables:\n" + "----------------\n"); + + qstring s; + for ( Py_ssize_t i=0; i + +// + +//------------------------------------------------------------------------- +/* +# +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? + // (Make sure it does not use custom formats because bit_string would be the format name) + if ( ri.bit_strings != NULL && (ri.flags & REGISTER_CUSTFMT) == 0 ) + { + 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 ( internal_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() || !PyW_GetNumber(py_ea, &ea) || !PyW_GetNumber(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)read_dbg_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) || !PyW_GetNumber(py_ea, &ea) ) + Py_RETURN_NONE; + + size_t sz = PyString_GET_SIZE(py_buf); + void *buf = (void *)PyString_AS_STRING(py_buf); + if ( write_dbg_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 ) + Py_RETURN_NONE; + else + 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; + get_dbg_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. In this case some requests like + memory read/write, bpt management succeed and register querying will fail. + Check if idaapi.get_process_state() < 0 to tell if the process is suspended + @return: Boolean + """ + pass +# +*/ +static bool dbg_can_query(); +PyObject *py_appcall( + ea_t func_ea, + thid_t tid, + PyObject *py_type, + PyObject *py_fields, + PyObject *arg_list); +// + +// +static PyObject *meminfo_vec_t_to_py(meminfo_vec_t &areas); +// + +// + +//------------------------------------------------------------------------- +/* +# +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 dbg_is_loaded(): + """ + Checks if a debugger is loaded + @return: Boolean + """ + pass +# +*/ +static bool dbg_is_loaded() +{ + return dbg != NULL; +} + +//------------------------------------------------------------------------- +/* +# +def refresh_debugger_memory(): + """ + Refreshes the debugger memory + @return: Nothing + """ + pass +# +*/ +static PyObject *refresh_debugger_memory() +{ + invalidate_dbgmem_config(); + invalidate_dbgmem_contents(BADADDR, 0); + + // Ask the debugger to populate debug names + if ( dbg != NULL && dbg->stopped_at_debug_event != NULL ) + dbg->stopped_at_debug_event(true); + + // Invalidate the cache + isEnabled(0); + + Py_RETURN_NONE; +} + +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(pid_t pid, thid_t tid, ea_t ea) { }; + 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; + int code = 0; + 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); + break; + + case dbg_process_exit: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_exit( + event->pid, + event->tid, + event->ea, + event->exit_code); + break; + + 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); + break; + + case dbg_process_detach: + event = va_arg(va, debug_event_t *); + proxy->dbg_process_detach( + event->pid, + event->tid, + event->ea); + break; + + case dbg_thread_start: + event = va_arg(va, debug_event_t *); + proxy->dbg_thread_start( + event->pid, + event->tid, + event->ea); + break; + + case dbg_thread_exit: + event = va_arg(va, debug_event_t *); + proxy->dbg_thread_exit( + event->pid, + event->tid, + event->ea, + event->exit_code); + break; + + 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); + break; + + case dbg_library_unload: + event = va_arg(va, debug_event_t *); + proxy->dbg_library_unload( + event->pid, + event->tid, + event->ea, + event->info); + break; + + case dbg_information: + event = va_arg(va, debug_event_t *); + proxy->dbg_information( + event->pid, + event->tid, + event->ea, + event->info); + break; + + case dbg_exception: + { + event = va_arg(va, debug_event_t *); + int *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); + break; + } + + case dbg_suspend_process: + proxy->dbg_suspend_process(); + break; + + case dbg_bpt: + { + thid_t tid = va_arg(va, thid_t); + ea_t breakpoint_ea = va_arg(va, ea_t); + int *warn = va_arg(va, int *); + *warn = proxy->dbg_bpt(tid, breakpoint_ea); + break; + } + + case dbg_trace: + { + thid_t tid = va_arg(va, thid_t); + ea_t ip = va_arg(va, ea_t); + code = proxy->dbg_trace(tid, ip); + break; + } + + 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); + break; + } + + case dbg_step_into: + proxy->dbg_step_into(); + break; + + case dbg_step_over: + proxy->dbg_step_over(); + break; + + case dbg_run_to: + event = va_arg(va, debug_event_t *); + proxy->dbg_run_to( + event->pid, + event->tid, + event->ea); + break; + + case dbg_step_until_ret: + proxy->dbg_step_until_ret(); + break; + } + } + catch (Swig::DirectorException &e) + { + msg("Exception in DBG Hook function: %s\n", e.getMessage()); + if (PyErr_Occurred()) + PyErr_Print(); + } + return code; +} +// +#endif diff --git a/pywraps/py_diskio.hpp b/pywraps/py_diskio.hpp new file mode 100644 index 0000000..899ecd5 --- /dev/null +++ b/pywraps/py_diskio.hpp @@ -0,0 +1,62 @@ +#ifndef __PY_IDA_DISKIO__ +#define __PY_IDA_DISKIO__ + +// +//-------------------------------------------------------------------------- +int idaapi py_enumerate_files_cb(const char *file, void *ud) +{ + PyObject *py_file = PyString_FromString(file); + PYW_GIL_ENSURE; + PyObject *py_ret = PyObject_CallFunctionObjArgs( + (PyObject *)ud, + py_file, + NULL); + PYW_GIL_RELEASE; + int r = (py_ret == NULL || !PyNumber_Check(py_ret)) ? 1 /* stop enumeration on failure */ : PyInt_AsLong(py_ret); + Py_XDECREF(py_file); + Py_XDECREF(py_ret); + return r; +} +// + +// +//-------------------------------------------------------------------------- +/* +# +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 + { + if ( !PyString_Check(path) || !PyString_Check(fname) || !PyCallable_Check(callback) ) + break; + + const char *_path = PyString_AsString(path); + const char *_fname = PyString_AsString(fname); + if ( _path == NULL || _fname == NULL ) + break; + + char answer[MAXSTR]; + answer[0] = '\0'; + int r = enumerate_files(answer, sizeof(answer), _path, _fname, py_enumerate_files_cb, callback); + return Py_BuildValue("(is)", r, answer); + } while ( false ); + Py_RETURN_NONE; +} +// + +#endif diff --git a/pywraps/py_diskio.py b/pywraps/py_diskio.py new file mode 100644 index 0000000..50ff54c --- /dev/null +++ b/pywraps/py_diskio.py @@ -0,0 +1,5 @@ +# +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/pywraps/py_expr.hpp b/pywraps/py_expr.hpp new file mode 100644 index 0000000..6c0688d --- /dev/null +++ b/pywraps/py_expr.hpp @@ -0,0 +1,156 @@ +#ifndef __PY_EXPR__ +#define __PY_EXPR__ + +// +struct py_idcfunc_ctx_t +{ + PyObject *py_func; + qstring name; + int nargs; + py_idcfunc_ctx_t(PyObject *py_func, const char *name, int nargs): py_func(py_func), name(name), nargs(nargs) + { + Py_INCREF(py_func); + } + ~py_idcfunc_ctx_t() + { + Py_DECREF(py_func); + } +}; + +//--------------------------------------------------------------------------- +static error_t py_call_idc_func( + void *_ctx, + idc_value_t *argv, + idc_value_t *r) +{ + // Convert IDC arguments to Python list + py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)_ctx; + int cvt; + ppyobject_vec_t pargs; + char errbuf[MAXSTR]; + if ( !pyw_convert_idc_args(argv, ctx->nargs, pargs, NULL, errbuf, sizeof(errbuf)) ) + { + // Error during conversion? Create an IDC exception + return PyW_CreateIdcException(r, errbuf); + } + + // Call the Python function + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallObject( + ctx->py_func, + pargs.empty() ? NULL : pargs[0]); + + error_t err; + if ( PyW_GetError(errbuf, sizeof(errbuf)) ) + { + err = PyW_CreateIdcException(r, errbuf); + Py_XDECREF(py_result); + } + else + { + // Convert the result to IDC + r->clear(); + cvt = pyvar_to_idcvar(py_result, r); + if ( cvt < CIP_OK ) + err = PyW_CreateIdcException(r, "ERROR: bad return value"); + else + err = eOk; + if ( cvt != CIP_OK_NODECREF ) + Py_XDECREF(py_result); + } + PYW_GIL_RELEASE; + + // Free the converted args + pyw_free_idc_args(pargs); + + return err; +} + +// + +// + +//--------------------------------------------------------------------------- +static size_t py_get_call_idc_func() +{ + return (size_t)py_call_idc_func; +} + +//--------------------------------------------------------------------------- +// Internal function: +// - capture the python callable +// - return a C context as a numeric value +static size_t pyw_register_idc_func( + const char *name, + const char *args, + PyObject *py_fp) +{ + return (size_t)new py_idcfunc_ctx_t(py_fp, name, strlen(args)); +} + +//--------------------------------------------------------------------------- +// Internal function: +// - free the C context +static bool pyw_unregister_idc_func(size_t ctxptr) +{ + // Unregister the function + py_idcfunc_ctx_t *ctx = (py_idcfunc_ctx_t *)ctxptr; + bool ok = set_idc_func_ex(ctx->name.c_str(), NULL, NULL, 0); + + // Delete the context + delete ctx; + + return ok; +} + +//--------------------------------------------------------------------------- +static bool py_set_idc_func_ex( + const char *name, + size_t fp_ptr, + const char *args, + int flags) +{ + return set_idc_func_ex(name, (idc_func_t *)fp_ptr, args, flags); +} + +//--------------------------------------------------------------------------- +// Compile* functions return false when error so the return +// value must be negated for the error string to be returned +bool CompileEx_wrap( + const char *file, + bool del_macros, + char *errbuf, size_t errbufsize) +{ + return !CompileEx(file, del_macros, errbuf, errbufsize); +} + +bool Compile_wrap(const char *file, char *errbuf, size_t errbufsize) +{ + return !Compile(file, errbuf, errbufsize); +} + +bool calcexpr_wrap( + ea_t where, + const char *line, + idc_value_t *rv, + char *errbuf, size_t errbufsize) +{ + return !calcexpr(where, line, rv, errbuf, errbufsize); +} + +bool calc_idc_expr_wrap( + ea_t where, + const char *line, + idc_value_t *rv, + char *errbuf, size_t errbufsize) +{ + return !calc_idc_expr(where, line, rv, errbuf, errbufsize); +} + +bool CompileLine_wrap(const char *line, char *errbuf, size_t errbufsize) +{ + return !CompileLineEx(line, errbuf, errbufsize); +} + +// +#endif \ No newline at end of file diff --git a/pywraps/py_expr.py b/pywraps/py_expr.py new file mode 100644 index 0000000..84f8373 --- /dev/null +++ b/pywraps/py_expr.py @@ -0,0 +1,169 @@ +# -------------------------------------------------------------------------- +import os +import sys + +import idaapi +import _idaapi +from sys import getrefcount +import gc + +try: + import pywraps + pywraps_there = True + _idaapi.pyw_register_idc_func = pywraps.pyw_register_idc_func + _idaapi.pyw_unregister_idc_func = pywraps.pyw_unregister_idc_func + _idaapi.py_get_call_idc_func = pywraps.py_get_call_idc_func + _idaapi.py_set_idc_func_ex = pywraps.py_set_idc_func_ex + + +except Exception as e: + pywraps_there = False + print("exception: %s" % str(e)) + + +print("Using PyWraps: %s" % pywraps_there) + +# -------------------------------------------------------------------------- +# +try: + import types + import ctypes + # Callback for IDC func callback (On Windows, we use stdcall) + # typedef error_t idaapi idc_func_t(idc_value_t *argv,idc_value_t *r); + _IDCFUNC_CB_T = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p) + + # A trampoline function that is called from idcfunc_t that will + # call the Python callback with the argv and r properly serialized to python + call_idc_func__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_call_idc_func()) +except: + def call_idc_func__(*args): + warning("IDC extensions need ctypes library in order to work") + return 0 + try: + _IDCFUNC_CB_T = CFUNCTYPE(c_int, c_void_p, c_void_p) + except: + _IDCFUNC_CB_T = None + + +# -------------------------------------------------------------------------- +EXTFUN_BASE = 0x0001 +"""requires open database""" +EXTFUN_NORET = 0x0002 +"""does not return. the interpreter may clean up its state before calling it.""" +EXTFUN_SAFE = 0x0004 +"""thread safe function. may be called""" + +# -------------------------------------------------------------------------- +class _IdcFunction(object): + """ + Internal class that calls pyw_call_idc_func() with a context + """ + def __init__(self, ctxptr): + self.ctxptr = ctxptr + # Take a reference to the ctypes callback + # (note: this will create a circular reference) + self.cb = _IDCFUNC_CB_T(self) + + fp_ptr = property(lambda self: ctypes.cast(self.cb, ctypes.c_void_p).value) + + def __call__(self, args, res): + return call_idc_func__(self.ctxptr, args, res) + + +# -------------------------------------------------------------------------- +# Dictionary to remember IDC function names along with the context pointer +# retrieved by using the internal pyw_register_idc_func() +__IDC_FUNC_CTXS = {} + +# -------------------------------------------------------------------------- +def set_idc_func_ex(name, fp=None, args=(), flags=0): + """ + Extends the IDC language by exposing a new IDC function that is backed up by a Python function + This function also unregisters the IDC function if 'fp' was passed as None + + @param name: IDC function name to expose + @param fp: Python callable that will receive the arguments and return a tuple. + If this argument is None then the IDC function is unregistered + @param args: Arguments. A tuple of idaapi.VT_XXX constants + @param flags: IDC function flags. A combination of EXTFUN_XXX constants + + @return: Boolean. + """ + global __IDC_FUNC_CTXS + + # Get the context + f = __IDC_FUNC_CTXS.get(name, None) + + # Unregistering? + if fp is None: + # Not registered? + if f is None: + return False + + # Break circular reference + del f.cb + + # Delete the name from the dictionary + del __IDC_FUNC_CTXS[name] + + # Delete the context and unregister the function + return _idaapi.pyw_unregister_idc_func(f.ctxptr) + + # Registering a function that is already registered? + if f is not None: + # Unregister it first + set_idc_func_ex(name, None) + + # Convert the tupple argument info to a string + args = "".join([chr(x) for x in args]) + + # Create a context + ctxptr = _idaapi.pyw_register_idc_func(name, args, fp) + if ctxptr == 0: + return False + + # Bind the context with the IdcFunc object + f = _IdcFunction(ctxptr) + + # Remember the Python context + __IDC_FUNC_CTXS[name] = f + + # Register IDC function with a callback + return _idaapi.py_set_idc_func_ex( + name, + f.fp_ptr, + args, + flags) + +# + +# -------------------------------------------------------------------------- +def test1(): + global MY_IDC_FUNC + try: + # Already registered? + MY_IDC_FUNC + # Unregister + print("Unregistering function") + set_idc_func_ex(MY_IDC_FUNC) + except: + MY_IDC_FUNC = "pysum" + + ok = set_idc_func_ex(MY_IDC_FUNC, my_idc_sum, (idaapi.VT_LONG, idaapi.VT_LONG), 0) + if not ok: + del MY_IDC_FUNC + + +# + +# -------------------------------------------------------------------------- +# +def py_power(n, e): + return n ** e + +ok = set_idc_func_ex("pow", py_power, (idaapi.VT_LONG, idaapi.VT_LONG), 0) +if ok: + print("Now the pow() will be present IDC!") +else: + print("Failed to register pow() IDC function") +# diff --git a/pywraps/py_gdl.py b/pywraps/py_gdl.py new file mode 100644 index 0000000..7371206 --- /dev/null +++ b/pywraps/py_gdl.py @@ -0,0 +1,88 @@ +# +# ----------------------------------------------------------------------- +class BasicBlock(object): + """Basic block class. It is returned by the Flowchart class""" + def __init__(self, id, bb, fc): + self._fc = fc + + 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._fc._q.calc_block_type(self.id) + """Block type (check fc_block_type_t enum)""" + + + def preds(self): + """ + Iterates the predecessors list + """ + q = self._fc._q + for i in xrange(0, self._fc._q.npred(self.id)): + yield self._fc[q.pred(self.id, i)] + + + def succs(self): + """ + Iterates the successors list + """ + q = self._fc._q + for i in xrange(0, q.nsucc(self.id)): + yield self._fc[q.succ(self.id, i)] + +# ----------------------------------------------------------------------- +class FlowChart(object): + """ + 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 (f is None) and (bounds is None or type(bounds) != types.TupleType): + raise Exception("Please specifiy either a function or start/end pair") + + if bounds is None: + bounds = (BADADDR, BADADDR) + + # Create the flowchart + self._q = qflow_chart_t("", f, bounds[0], bounds[1], flags) + + size = property(lambda self: self._q.size()) + """Number of blocks in the flow chart""" + + + def refresh(): + """Refreshes the flow chart""" + self._q.refresh() + + + def _getitem(self, index): + return BasicBlock(index, self._q[index], self) + + + def __iter__(self): + return (self._getitem(index) for index in xrange(0, self.size)) + + + def __getitem__(self, index): + """ + Returns a basic block + + @return: BasicBlock + """ + if index >= self.size: + raise KeyError + else: + return self._getitem(index) + +# diff --git a/pywraps/py_graph.hpp b/pywraps/py_graph.hpp new file mode 100644 index 0000000..bfd9adc --- /dev/null +++ b/pywraps/py_graph.hpp @@ -0,0 +1,816 @@ +#ifndef __PY_IDA_GRAPH__ +#define __PY_IDA_GRAPH__ + +// +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() + { + // We start by one and keep zero for error id + uid = 1; + } + + 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 + PYW_GIL_ENSURE; + PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_COMMAND, "n", id); + PYW_GIL_RELEASE; + 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 + PYW_GIL_ENSURE; + PyObject *ret = PyObject_CallMethod(self, (char *)S_ON_REFRESH, NULL); + PYW_GIL_RELEASE; + if ( ret == NULL || !PyObject_IsTrue(ret) ) + { + Py_XDECREF(ret); + return; + } + + // Refer to the nodes + PyObject *nodes = PyW_TryGetAttrString(self, S_M_NODES); + if ( ret == NULL || !PyList_Check(nodes) ) + { + Py_XDECREF(nodes); + return; + } + + // Refer to the edges + PyObject *edges = PyW_TryGetAttrString(self, S_M_EDGES); + if ( ret == NULL || !PyList_Check(edges) ) + { + 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 + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallMethod(self, (char *)S_ON_GETTEXT, "i", node); + PYW_GIL_RELEASE; + 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; + + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallMethod(self, (char *)S_ON_HINT, "i", mousenode); + PYW_GIL_RELEASE; + bool ok = result != NULL && PyString_Check(result); + if ( !ok ) + { + 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) != 0 ) + { + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL); + PYW_GIL_RELEASE; + + Py_XDECREF(result); + } + unbind(); + } + + // Remove the TForm from list + if ( form != NULL ) + tform_pyg.erase(form); + + // Remove all associated commands from the list + 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; + + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallMethod( + self, + (char *)S_ON_CLICK, + "i", + item2->n); + PYW_GIL_RELEASE; + + if ( result == NULL || !PyObject_IsTrue(result) ) + { + Py_XDECREF(result); + 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; + + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallMethod( + self, + (char *)S_ON_DBL_CLICK, + "i", + item->node); + PYW_GIL_RELEASE; + + if ( result == NULL || !PyObject_IsTrue(result) ) + { + Py_XDECREF(result); + return 1; + } + Py_DECREF(result); + return 0; + } + + // a graph viewer got focus + void on_gotfocus(graph_viewer_t * /*gv*/) + { + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallMethod( + self, + (char *)S_ON_ACTIVATE, + NULL); + PYW_GIL_RELEASE; + Py_XDECREF(result); + } + + // a graph viewer lost focus + void on_lostfocus(graph_viewer_t * /*gv*/) + { + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallMethod( + self, + (char *)S_ON_DEACTIVATE, + NULL); + PYW_GIL_RELEASE; + + 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; + + PYW_GIL_ENSURE; + PyObject *result = PyObject_CallMethod( + self, + (char *)S_ON_SELECT, + "i", + curnode); + PYW_GIL_RELEASE; + + bool allow = (result != NULL && PyObject_IsTrue(result)); + Py_XDECREF(result); + return allow ? 0 : 1; + } + + 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 + { + // Ignore the click + ret = 1; + } + 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 = PyW_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); + } + + 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; + char grnode[MAXSTR]; + qsnprintf(grnode, sizeof(grnode), "$ pygraph %s", title); + id.create(grnode); + 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, FORM_CLOSE_LATER); + } + + 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); + + // New instance? + if ( ret == NULL ) + { + qstring title; + if ( !PyW_GetStringAttr(self, S_M_TITLE, &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); +} +// + +//-------------------------------------------------------------------------- + +// +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 diff --git a/pywraps/py_graph.py b/pywraps/py_graph.py new file mode 100644 index 0000000..8a50b55 --- /dev/null +++ b/pywraps/py_graph.py @@ -0,0 +1,169 @@ +# +class GraphViewer(object): + """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 __iter__(self): + return (self._nodes[index] for index in xrange(0, len(self._nodes))) + + + def __getitem__(self, idx): + """Returns a reference to the object associated with this node id""" + if idx >= len(self._nodes): + raise KeyError + else: + 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 +# +# \ No newline at end of file diff --git a/pywraps/py_idaapi.hpp b/pywraps/py_idaapi.hpp new file mode 100644 index 0000000..8e61bcc --- /dev/null +++ b/pywraps/py_idaapi.hpp @@ -0,0 +1,804 @@ +#ifndef __PY_IDAAPI__ +#define __PY_IDAAPI__ + +// + +//------------------------------------------------------------------------ +// String constants used +static const char S_PYINVOKE0[] = "_py_invoke0"; +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_PROPS[] = "props"; +static const char S_NAME[] = "name"; +static const char S_TITLE[] = "title"; +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_EMBEDDED[] = "embedded"; +static const char S_POPUP_NAMES[] = "popup_names"; +static const char S_FLAGS[] = "flags"; +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_COMPLETE_LINE[] = "OnCompleteLine"; +static const char S_ON_CREATE[] = "OnCreate"; +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_REFRESHED[] = "OnRefreshed"; +static const char S_ON_EXECUTE_LINE[] = "OnExecuteLine"; +static const char S_ON_SELECT_LINE[] = "OnSelectLine"; +static const char S_ON_SELECTION_CHANGE[] = "OnSelectionChange"; +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 + +//------------------------------------------------------------------------ +static PyObject *py_cvt_helper_module = NULL; +static bool pywraps_initialized = false; + +//--------------------------------------------------------------------------- +// Context structure used by add|del_menu_item() +struct py_add_del_menu_item_ctx +{ + qstring menupath; + PyObject *cb_data; +}; + +//--------------------------------------------------------------------------- +// Context structure used by add|del_idc_hotkey() +struct py_idchotkey_ctx_t +{ + qstring hotkey; + PyObject *pyfunc; +}; + +//--------------------------------------------------------------------------- +// Context structure used by register/unregister timer +struct py_timer_ctx_t +{ + qtimer_t timer_id; + PyObject *pycallback; +}; + +//------------------------------------------------------------------------ +const char *pywraps_check_autoscripts() +{ + static const char *exts[] = {"py", "pyw", "pyc", "pyo"}; + + static const char *fns[] = + { + "swig_runtime_data" SWIG_RUNTIME_VERSION, + "sitecustomize", + "usercustomize" + }; + + for (size_t ifn=0; ifn < qnumber(fns); ++ifn ) + { + for ( size_t iext=0; iext < qnumber(exts); ++iext ) + { + static char fn[QMAXPATH]; + qsnprintf(fn, sizeof(fn), "%s.%s", fns[ifn], exts[iext]); + if ( qfileexist(fn) ) + return fn; + } + } + return NULL; +} + +//------------------------------------------------------------------------ +error_t PyW_CreateIdcException(idc_value_t *res, const char *msg) +{ + // Create exception object + VarObject(res, find_idc_class("exception")); + + // Set the message field + idc_value_t v; + v.set_string(msg); + VarSetAttr(res, "message", &v); + + // Throw exception + return set_qerrno(eExecThrow); +} + +//------------------------------------------------------------------------ +// Calls a Python callable encoded in IDC.pvoid member +static const char idc_py_invoke0_args[] = { VT_PVOID, 0 }; +static error_t idaapi idc_py_invoke0( + idc_value_t *argv, + idc_value_t *res) +{ + PyObject *pyfunc = (PyObject *) argv[0].pvoid; + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallFunctionObjArgs(pyfunc, NULL); + PYW_GIL_RELEASE; + + Py_XDECREF(py_result); + + // Report Python error as IDC exception + qstring err; + if ( PyW_GetError(&err) ) + return PyW_CreateIdcException(res, err.c_str()); + + 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 = PyW_TryImportModule(S_PY_IDAAPI_MODNAME); + if ( py_cvt_helper_module == NULL ) + return false; + } + + // Register the IDC PyInvoke0 method (helper function for add_idc_hotkey()) + if ( !set_idc_func_ex(S_PYINVOKE0, idc_py_invoke0, idc_py_invoke0_args, 0) ) + return false; + + // IDC opaque class not registered? + 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; + + // Unregister the IDC PyInvoke0 method (helper function for add_idc_hotkey()) + set_idc_func_ex(S_PYINVOKE0, NULL, idc_py_invoke0_args, 0); +} + +//------------------------------------------------------------------------ +// 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; + + PYW_GIL_ENSURE; + PyObject *py_obj = PyObject_CallFunctionObjArgs(py_cls, NULL); + PYW_GIL_RELEASE; + + Py_DECREF(py_cls); + if ( PyW_GetError() || 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); + PYW_GIL_ENSURE; + PyObject *py_obj = PyObject_CallFunctionObjArgs(py_cls, py_lnk, NULL); + PYW_GIL_RELEASE; + Py_DECREF(py_cls); + Py_DECREF(py_lnk); + + if ( PyW_GetError() || 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 +PyObject *get_idaapi_attr_by_id(const int class_id) +{ + if ( class_id >= PY_CLSID_LAST || py_cvt_helper_module == NULL ) + 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 py_cvt_helper_module == NULL + ? NULL + : PyW_TryGetAttrString(py_cvt_helper_module, attrname); +} + +//------------------------------------------------------------------------ +// Returns a qstring from an object attribute +bool PyW_GetStringAttr( + PyObject *py_obj, + const char *attr_name, + qstring *str) +{ + PyObject *py_attr = PyW_TryGetAttrString(py_obj, attr_name); + if ( py_attr == NULL ) + return false; + + bool ok = PyString_Check(py_attr) != 0; + if ( ok ) + *str = PyString_AsString(py_attr); + + Py_DECREF(py_attr); + return ok; +} + +//------------------------------------------------------------------------ +// Returns an attribute or NULL +// No errors will be set if the attribute did not exist +PyObject *PyW_TryGetAttrString(PyObject *py_obj, const char *attr) +{ + if ( !PyObject_HasAttrString(py_obj, attr) ) + return NULL; + else + return PyObject_GetAttrString(py_obj, attr); +} + +//------------------------------------------------------------------------ +// Tries to import a module and clears the exception on failure +PyObject *PyW_TryImportModule(const char *name) +{ + PYW_GIL_ENSURE; + PyObject *result = PyImport_ImportModule(name); + PYW_GIL_RELEASE; + 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 PyW_GetNumberAsIDC(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 PyW_GetNumber(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(); + + // Try to parse as int64 + 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(); + + // Try to parse as uint64 + unsigned PY_LONG_LONG ull = PyLong_AsUnsignedLongLong(py_var); + PyObject *err = PyErr_Occurred(); + if ( err == NULL ) + { + if ( num != NULL ) + *num = uint64(ull); + if ( is_64 != NULL ) + *is_64 = true; + return true; + } + // Negative number? _And_ it with uint64(-1) + bool ok = false; + if ( err == PyExc_TypeError ) + { + PyObject *py_mask = Py_BuildValue("K", 0xFFFFFFFFFFFFFFFFull); + PyObject *py_num = PyNumber_And(py_var, py_mask); + if ( py_num != NULL && py_mask != NULL ) + { + PyErr_Clear(); + ull = PyLong_AsUnsignedLongLong(py_num); + if ( !PyErr_Occurred() ) + { + if ( num != NULL ) + *num = uint64(ull); + if ( is_64 != NULL ) + *is_64 = true; + ok = true; + } + } + Py_XDECREF(py_num); + Py_XDECREF(py_mask); + } + PyErr_Clear(); + return ok; +} + +//------------------------------------------------------------------------- +// Checks if a given object is of sequence type +bool PyW_IsSequenceType(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 PyW_ObjectToString(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 PyW_GetError(qstring *out, bool clear_err) +{ + if ( PyErr_Occurred() == NULL ) + return false; + + // Error occurred but details not needed? + if ( out == NULL ) + { + // Just clear the error + if ( clear_err ) + PyErr_Clear(); + return true; + } + + // Get the exception info + PyObject *err_type, *err_value, *err_traceback, *py_ret(NULL); + PyErr_Fetch(&err_type, &err_value, &err_traceback); + + if ( !clear_err ) + PyErr_Restore(err_type, err_value, err_traceback); + + // Resolve FormatExc() + PyObject *py_fmtexc = get_idaapi_attr(S_IDAAPI_FORMATEXC); + + // Helper there? + if ( py_fmtexc != NULL ) + { + // Call helper + PYW_GIL_ENSURE; + py_ret = PyObject_CallFunctionObjArgs( + py_fmtexc, + err_type, + err_value, + err_traceback, + NULL); + PYW_GIL_RELEASE; + + // Dispose helper reference + Py_DECREF(py_fmtexc); + } + + // Clear the error + if ( clear_err ) + PyErr_Clear(); + + // Helper failed?! + if ( py_ret == NULL ) + { + // Just convert the 'value' part of the original error + py_ret = PyObject_Str(err_value); + } + + // No exception text? + if ( py_ret == NULL ) + { + *out = "IDAPython: unknown error!"; + } + else + { + *out = PyString_AsString(py_ret); + Py_DECREF(py_ret); + } + + if ( clear_err ) + { + Py_XDECREF(err_traceback); + Py_XDECREF(err_value); + Py_XDECREF(err_type); + } + return true; +} + +//------------------------------------------------------------------------- +bool PyW_GetError(char *buf, size_t bufsz, bool clear_err) +{ + qstring s; + if ( !PyW_GetError(&s, clear_err) ) + return false; + + qstrncpy(buf, s.c_str(), bufsz); + return true; +} + +//------------------------------------------------------------------------- +// A loud version of PyGetError() which gets the error and displays it +// This method is used to display errors that occurred in a callback +bool PyW_ShowCbErr(const char *cb_name) +{ + static qstring err_str; + if ( !PyW_GetError(&err_str) ) + return false; + + warning("IDAPython: Error while calling Python callback <%s>:\n%s", cb_name, err_str.c_str()); + return true; +} + +//--------------------------------------------------------------------------- +void *pyobj_get_clink(PyObject *pyobj) +{ + // Try to query the link attribute + PyObject *attr = PyW_TryGetAttrString(pyobj, S_CLINK_NAME); + void *t = attr != NULL && PyCObject_Check(attr) ? PyCObject_AsVoidPtr(attr) : NULL; + Py_XDECREF(attr); + return t; +} + +// + +// +//------------------------------------------------------------------------ +/* +# +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. The script wait box dialog will be hidden and shown again when the timeout elapses. + See also L{disable_script_timeout}. + + @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 disable_script_timeout(): + """ + Disables the script timeout and hides the script wait box. + Calling L{set_script_timeout} will not have any effects until the script is compiled and executed again + + @return: None + """ + pass +# +*/ +void disable_script_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 +# +*/ + +//--------------------------------------------------------------------------- +// qstrvec_t wrapper +//--------------------------------------------------------------------------- +DECLARE_PY_CLINKED_OBJECT(qstrvec_t); + +static bool qstrvec_t_assign(PyObject *self, PyObject *other) +{ + qstrvec_t *lhs = qstrvec_t_get_clink(self); + qstrvec_t *rhs = qstrvec_t_get_clink(other); + if (lhs == NULL || rhs == NULL) + return false; + *lhs = *rhs; + return true; +} + +static PyObject *qstrvec_t_addressof(PyObject *self, size_t idx) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + Py_RETURN_NONE; + else + return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)&sv->at(idx)); +} + + +static bool qstrvec_t_set( + PyObject *self, + size_t idx, + const char *s) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + return false; + (*sv)[idx] = s; + return true; +} + +static bool qstrvec_t_from_list( + PyObject *self, + PyObject *py_list) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + return sv == NULL ? false : PyW_PyListToStrVec(py_list, *sv); +} + +static size_t qstrvec_t_size(PyObject *self) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + return sv == NULL ? 0 : sv->size(); +} + +static PyObject *qstrvec_t_get(PyObject *self, size_t idx) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + Py_RETURN_NONE; + return PyString_FromString(sv->at(idx).c_str()); +} + +static bool qstrvec_t_add(PyObject *self, const char *s) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL ) + return false; + sv->push_back(s); + return true; +} + +static bool qstrvec_t_clear(PyObject *self, bool qclear) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL ) + return false; + + if ( qclear ) + sv->qclear(); + else + sv->clear(); + + return true; +} + +static bool qstrvec_t_insert( + PyObject *self, + size_t idx, + const char *s) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + return false; + sv->insert(sv->begin() + idx, s); + return true; +} + +static bool qstrvec_t_remove(PyObject *self, size_t idx) +{ + qstrvec_t *sv = qstrvec_t_get_clink(self); + if ( sv == NULL || idx >= sv->size() ) + return false; + + sv->erase(sv->begin()+idx); + return true; +} + +//--------------------------------------------------------------------------- +// + +#endif diff --git a/pywraps/py_idaapi.py b/pywraps/py_idaapi.py new file mode 100644 index 0000000..3744f86 --- /dev/null +++ b/pywraps/py_idaapi.py @@ -0,0 +1,513 @@ +# ----------------------------------------------------------------------- +try: + import pywraps + pywraps_there = True +except: + pywraps_there = False + +import _idaapi +import random +import operator +import datetime + +# + +import struct +import traceback +import os +import sys +import bisect +import __builtin__ + +# ----------------------------------------------------------------------- + +# 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""" + +# Step trace options (used with set_step_trace_options()) +ST_OVER_DEBUG_SEG = 0x01 +"""step tracing will be disabled when IP is in a debugger segment""" + +ST_OVER_LIB_FUNC = 0x02 +"""step tracing will be disabled when IP is in a library function""" + +# ----------------------------------------------------------------------- +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)""" + self._free() + + def _free(self): + """Explicitly delete the link (only if not static)""" + if not self.__static_clink__ and self.__clink__ is not None: + self._del_clink(self.__clink__) + self.__clink__ = None + + 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 + + # + # Methods to be overwritten + # + 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 _get_clink_ptr(self): + """ + Overwrite me. + Returns the C link pointer as a 64bit number + """ + pass + + 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__) + """Returns the C link as a PyObject""" + + clink_ptr = property(lambda self: self._get_clink_ptr()) + """Returns the C link pointer as a number""" + +# ----------------------------------------------------------------------- +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) + +# ----------------------------------------------------------------------- +# qstrvec_t clinked object +class qstrvec_t(py_clinked_object_t): + """Class representing an qstrvec_t""" + + def __init__(self, items=None): + py_clinked_object_t.__init__(self) + # Populate the list if needed + if items: + self.from_list(items) + + def _create_clink(self): + return _idaapi.qstrvec_t_create() + + def _del_clink(self, lnk): + return _idaapi.qstrvec_t_destroy(lnk) + + def _get_clink_ptr(self): + return _idaapi.qstrvec_t_get_clink_ptr(self) + + def assign(self, other): + """Copies the contents of 'other' to 'self'""" + return _idaapi.qstrvec_t_assign(self, other) + + def __setitem__(self, idx, s): + """Sets string at the given index""" + return _idaapi.qstrvec_t_set(self, idx, s) + + def __getitem__(self, idx): + """Gets the string at the given index""" + return _idaapi.qstrvec_t_get(self, idx) + + def __get_size(self): + return _idaapi.qstrvec_t_size(self) + + size = property(__get_size) + """Returns the count of elements""" + + def addressof(self, idx): + """Returns the address (as number) of the qstring at the given index""" + return _idaapi.qstrvec_t_addressof(self, idx) + + def add(self, s): + """Add a string to the vector""" + return _idaapi.qstrvec_t_add(self, s) + + + def from_list(self, lst): + """Populates the vector from a Python string list""" + return _idaapi.qstrvec_t_from_list(self, lst) + + + def clear(self, qclear=False): + """ + Clears all strings from the vector. + @param qclear: Just reset the size but do not actually free the memory + """ + return _idaapi.qstrvec_t_clear(self, qclear) + + + def insert(self, idx, s): + """Insert a string into the vector""" + return _idaapi.qstrvec_t_insert(self, idx, s) + + + def remove(self, idx): + """Removes a string from the vector""" + return _idaapi.qstrvec_t_remove(self, idx) + +# ----------------------------------------------------------------------- +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('\x00') + return val if n == -1 else 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 (0-based) + @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 = ~(((1 << (e-s+1))-1) << s) + + 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 n not 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_ExecSystem(cmd): + """ + Executes a command with popen(). + """ + try: + f = os.popen(cmd, "r") + s = ''.join(f.readlines()) + f.close() + return s + except Exception as e: + return "%s\n%s" % (str(e), traceback.format_exc()) + +# ------------------------------------------------------------ +def IDAPython_FormatExc(etype, value, tb, limit=None): + """ + This function is used to format an exception given the + values returned by a PyErr_Fetch() + """ + try: + return ''.join(traceback.format_exception(etype, value, tb, limit)) + except: + return str(value) + + +# ------------------------------------------------------------ +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 + + try: + execfile(script, g) + PY_COMPILE_ERR = None + except Exception as e: + PY_COMPILE_ERR = "%s\n%s" % (str(e), traceback.format_exc()) + print(PY_COMPILE_ERR) + 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 + +# ---------------------------------------------------------------------- +class __IDAPython_Completion_Util(object): + """Internal utility class for auto-completion support""" + def __init__(self): + self.n = 0 + self.completion = None + self.lastmodule = None + + @staticmethod + def parse_identifier(line, prefix, prefix_start): + """ + Parse a line and extracts identifier + """ + id_start = prefix_start + while id_start > 0: + ch = line[id_start] + if not ch.isalpha() and ch != '.' and ch != '_': + id_start += 1 + break + id_start -= 1 + + return line[id_start:prefix_start + len(prefix)] + + @staticmethod + def dir_of(m, prefix): + return [x for x in dir(m) if x.startswith(prefix)] + + @classmethod + def get_completion(cls, id, prefix): + try: + m = sys.modules['__main__'] + + parts = id.split('.') + c = len(parts) + + for i in xrange(0, c-1): + m = getattr(m, parts[i]) + except Exception as e: + return (None, None) + else: + # search in the module + completion = cls.dir_of(m, prefix) + + # no completion found? looking from the global scope? then try the builtins + if not completion and c == 1: + completion = cls.dir_of(__builtin__, prefix) + + return (m, completion) if completion else (None, None) + + def __call__(self, prefix, n, line, prefix_start): + if n == 0: + self.n = n + id = self.parse_identifier(line, prefix, prefix_start) + self.lastmodule, self.completion = self.get_completion(id, prefix) + + if self.completion is None or n >= len(self.completion): + return None + + s = self.completion[n] + try: + attr = getattr(self.lastmodule, s) + # Is it callable? + if callable(attr): + return s + ("" if line.startswith("?") else "(") + # Is it iterable? + elif isinstance(attr, basestring) or getattr(attr, '__iter__', False): + return s + "[" + except: + pass + + return s + +# Instantiate a completion object +IDAPython_Completion = __IDAPython_Completion_Util() + +# diff --git a/pywraps/py_idp.hpp b/pywraps/py_idp.hpp new file mode 100644 index 0000000..58c65f4 --- /dev/null +++ b/pywraps/py_idp.hpp @@ -0,0 +1,1536 @@ +#ifndef __PY_IDP__ +#define __PY_IDP__ + +//------------------------------------------------------------------------- +// +//------------------------------------------------------------------------- + +//------------------------------------------------------------------------- +/* +# +def AssembleLine(ea, cs, ip, use32, line): + """ + Assemble an instruction to a string (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 assemble(ea, cs, ip, use32, line): + """ + Assemble an instruction into the database (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: Boolean. True on success. + """ +# +*/ +bool 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 true; + } + } + return false; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_id(): + """ + Returns the 'ph.id' field + """ + pass +# +*/ +static size_t ph_get_id() +{ + return ph.id; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_version(): + """ + Returns the 'ph.version' + """ + pass +# +*/ +static size_t ph_get_version() +{ + return ph.version; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_flag(): + """ + Returns the 'ph.flag' + """ + pass +# +*/ +static size_t ph_get_flag() +{ + return ph.flag; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_cnbits(): + """ + Returns the 'ph.cnbits' + """ + pass +# +*/ +static size_t ph_get_cnbits() +{ + return ph.cnbits; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_dnbits(): + """ + Returns the 'ph.dnbits' + """ + pass +# +*/ +static size_t ph_get_dnbits() +{ + return ph.dnbits; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_regFirstSreg(): + """ + Returns the 'ph.regFirstSreg' + """ + pass +# +*/ +static size_t ph_get_regFirstSreg() +{ + return ph.regFirstSreg; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_regLastSreg(): + """ + Returns the 'ph.regLastSreg' + """ + pass +# +*/ +static size_t ph_get_regLastSreg() +{ + return ph.regLastSreg; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_segreg_size(): + """ + Returns the 'ph.segreg_size' + """ + pass +# +*/ +static size_t ph_get_segreg_size() +{ + return ph.segreg_size; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_regCodeSreg(): + """ + Returns the 'ph.regCodeSreg' + """ + pass +# +*/ +static size_t ph_get_regCodeSreg() +{ + return ph.regCodeSreg; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_regDataSreg(): + """ + Returns the 'ph.regDataSreg' + """ + pass +# +*/ +static size_t ph_get_regDataSreg() +{ + return ph.regDataSreg; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_high_fixup_bits(): + """ + Returns the 'ph.high_fixup_bits' + """ + pass +# +*/ +static size_t ph_get_high_fixup_bits() +{ + return ph.high_fixup_bits; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_icode_return(): + """ + Returns the 'ph.icode_return' + """ + pass +# +*/ +static size_t ph_get_icode_return() +{ + return ph.icode_return; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_instruc_start(): + """ + Returns the 'ph.instruc_start' + """ + pass +# +*/ +static size_t ph_get_instruc_start() +{ + return ph.instruc_start; +} + +//------------------------------------------------------------------------- +/* +# +def ph_get_instruc_end(): + """ + Returns the 'ph.instruc_end' + """ + pass +# +*/ +static size_t ph_get_instruc_end() +{ + return ph.instruc_end; +} + +//------------------------------------------------------------------------- +/* +# +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_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 +def ph_get_operand_info(): + """ + Returns the operand information given an ea and operand number. + + @param ea: address + @param n: operand number + + @return: Returns an idd_opinfo_t as a tuple: (modified, ea, reg_ival, regidx, value_size). + Please refer to idd_opinfo_t structure in the SDK. + """ + pass +# +*/ +static PyObject *ph_get_operand_info( + ea_t ea, + int n) +{ + do + { + if ( dbg == NULL || n == - 1 ) + break; + + // Allocate register space + thid_t tid = get_current_thread(); + regvals_t regvalues; + regvalues.resize(dbg->registers_size); + // Read registers + if ( get_reg_vals(tid, -1, regvalues.begin()) != 1 ) + break; + + // Call the processor module + idd_opinfo_t opinf; + if ( ph.notify(ph.get_operand_info, + ea, + n, + tid, + _py_getreg, + regvalues.begin(), + &opinf) != 0 ) + { + break; + } + return Py_BuildValue("(i" PY_FMT64 "Kii)", + opinf.modified, + opinf.ea, + opinf.value.ival, + opinf.debregidx, + opinf.value_size); + } while (false); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +/* +# +class IDP_Hooks(object): + def hook(self): + """ + Creates an IDP hook + + @return: Boolean true on success + """ + pass + + + def unhook(self): + """ + Removes the IDP hook + @return: Boolean true on success + """ + pass + + + 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) + """ + pass + + 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 + """ + pass + + + 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 may_be_func(self, no_crefs): + """ + Can a function start here? + @param state: autoanalysis phase + 0: creating functions + 1: creating chunks + + @return: integer (probability 0..100) + """ + pass + + + def closebase(self): + """ + The database will be closed now + """ + pass + + + def savebase(self): + """ + The database is being saved. Processor module should + """ + pass + + + def rename(self, ea, new_name): + """ + The kernel is going to rename a byte. + + @param ea: Address + @param new_name: The new name + + @return: + - If returns value <=0, then the kernel should + not rename it. See also the 'renamed' event + """ + pass + + + def renamed(self, ea, new_name, local_name): + """ + The kernel has renamed a byte + + @param ea: Address + @param new_name: The new name + @param local_name: Is local name + + @return: Ignored + """ + pass + + + def undefine(self, ea): + """ + An item in the database (insn or data) is being deleted + @param ea: Address + @return: + - returns: >0-ok, <=0-the kernel should stop + - if the return value is positive: + bit0 - ignored + bit1 - do not delete srareas at the item end + """ + pass + + + def make_code(self, ea, size): + """ + An instruction is being created + @param ea: Address + @param size: Instruction size + @return: 1-ok, <=0-the kernel should stop + """ + pass + + + def make_code(self, ea, size): + """ + An instruction is being created + @param ea: Address + @param size: Instruction size + @return: 1-ok, <=0-the kernel should stop + """ + pass + + + def make_data(self, ea, flags, tid, len): + """ + A data item is being created + @param ea: Address + @param tid: type id + @param flags: item flags + @param len: data item size + @return: 1-ok, <=0-the kernel should stop + """ + pass + + + def load_idasgn(self, short_sig_name): + """ + FLIRT signature have been loaded for normal processing + (not for recognition of startup sequences) + @param short_sig_name: signature name + @return: Ignored + """ + pass + + + def add_func(self, func): + """ + The kernel has added a function + @param func: the func_t instance + @return: Ignored + """ + pass + + + def del_func(self, func): + """ + The kernel is about to delete a function + @param func: the func_t instance + @return: 1-ok,<=0-do not delete + """ + pass + + + def is_call_insn(self, ea, func_name): + """ + Is the instruction a "call"? + + @param ea: instruction address + @return: 1-unknown, 0-no, 2-yes + """ + pass + + + def is_ret_insn(self, ea, func_name): + """ + Is the instruction a "return"? + + @param ea: instruction address + @param strict: - True: report only ret instructions + False: include instructions like "leave" which begins the function epilog + @return: 1-unknown, 0-no, 2-yes + """ + pass + + + def assemble(self, ea, cs, ip, use32, line): + """ + Assembles an instruction + + @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 to let the underlying processor module assemble the line + - or a string containing the assembled buffer + """ + pass + +# +*/ +//--------------------------------------------------------------------------- +// IDP hooks +//--------------------------------------------------------------------------- +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() + { + Py_RETURN_NONE; + } + + virtual int is_sane_insn(int no_crefs) + { + return 0; + } + + virtual int may_be_func(int state) + { + return 0; + } + + virtual int closebase() + { + return 0; + } + + virtual void savebase() + { + } + + virtual int rename(ea_t ea, const char *new_name) + { + return 0; + } + + virtual void renamed(ea_t ea, const char *new_name, bool local_name) + { + } + + virtual int undefine(ea_t ea) + { + return 0; + } + + virtual int make_code(ea_t ea, asize_t size) + { + return 0; + } + + virtual int make_data(ea_t ea, flags_t flags, tid_t tid, asize_t len) + { + return 0; + } + + virtual void load_idasgn(const char *short_sig_name) + { + } + + virtual void add_func(func_t *func) + { + } + + virtual int del_func(func_t *func) + { + return 0; + } + + virtual int is_call_insn(ea_t /*ea*/) + { + return 0; + } + + virtual int is_ret_insn(ea_t /*ea*/, bool /*strict*/) + { + return 0; + } + + virtual PyObject *assemble( + ea_t /*ea*/, + ea_t /*cs*/, + ea_t /*ip*/, + bool /*use32*/, + const char * /*line*/) + { + Py_RETURN_NONE; + } +}; + +//--------------------------------------------------------------------------- +// IDB hooks +//--------------------------------------------------------------------------- +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*/, ea_t /*offset*/) { 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 IDP_Callback(void *ud, int notification_code, va_list va) +{ + IDP_Hooks *proxy = (IDP_Hooks *)ud; + int ret = 0; + try + { + switch ( notification_code ) + { + 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; + } + + case processor_t::closebase: + { + proxy->closebase(); + break; + } + + case processor_t::savebase: + { + proxy->savebase(); + break; + } + + case processor_t::rename: + { + ea_t ea = va_arg(va, ea_t); + const char *new_name = va_arg(va, const char *); + ret = proxy->rename(ea, new_name); + break; + } + + case processor_t::renamed: + { + ea_t ea = va_arg(va, ea_t); + const char *new_name = va_arg(va, const char *); + bool local_name = va_argi(va, bool); + proxy->renamed(ea, new_name, local_name); + break; + } + + case processor_t::undefine: + { + ea_t ea = va_arg(va, ea_t); + ret = proxy->undefine(ea); + break; + } + + case processor_t::make_code: + { + ea_t ea = va_arg(va, ea_t); + asize_t size = va_arg(va, asize_t); + ret = proxy->make_code(ea, size); + break; + } + + case processor_t::make_data: + { + ea_t ea = va_arg(va, ea_t); + flags_t flags = va_arg(va, flags_t); + tid_t tid = va_arg(va, tid_t); + asize_t len = va_arg(va, asize_t); + ret = proxy->make_data(ea, flags, tid, len); + break; + } + + case processor_t::load_idasgn: + { + const char *short_sig_name = va_arg(va, const char *); + proxy->load_idasgn(short_sig_name); + break; + } + + case processor_t::add_func: + { + func_t *func = va_arg(va, func_t *); + proxy->add_func(func); + break; + } + + case processor_t::del_func: + { + func_t *func = va_arg(va, func_t *); + ret = proxy->del_func(func); + break; + } + + case processor_t::is_call_insn: + { + ea_t ea = va_arg(va, ea_t); + ret = proxy->is_call_insn(ea); + break; + } + + case processor_t::is_ret_insn: + { + ea_t ea = va_arg(va, ea_t); + bool strict = va_argi(va, bool); + ret = proxy->is_ret_insn(ea, strict); + break; + } + + case processor_t::assemble: + { + ea_t ea = va_arg(va, ea_t); + ea_t cs = va_arg(va, ea_t); + ea_t ip = va_arg(va, ea_t); + bool use32 = va_argi(va, bool); + const char *line = va_arg(va, const char *); + // Extract user buffer (we hardcode the MAXSTR size limit) + uchar *bin = va_arg(va, uchar *); + // Call python + PyObject *py_buffer = proxy->assemble(ea, cs, ip, use32, line); + if ( py_buffer != NULL && PyString_Check(py_buffer) ) + { + char *s; + Py_ssize_t len; + if ( PyString_AsStringAndSize(py_buffer, &s, &len) != -1 ) + { + if ( len > MAXSTR ) + len = MAXSTR; + memcpy(bin, s, len); + ret = len; + } + } + // ret = 0 otherwise + Py_XDECREF(py_buffer); + break; + } + // validate_flirt_func, // flirt has recognized a library function + // // this callback can be used by a plugin or proc module + // // to intercept it and validate such a function + // // args: ea_t start_ea + // // const char *funcname + // // returns: -1-do not create a function, + // // 1-function is validated + // // the idp module is allowed to modify 'cmd' + // set_func_start, // Function chunk start address will be changed + // // args: func_t *pfn + // // ea_t new_start + // // Returns: 1-ok,<=0-do not change + // set_func_end, // Function chunk end address will be changed + // // args: func_t *pfn + // // ea_t new_end + // // Returns: 1-ok,<=0-do not change + // outlabel, // The kernel is going to generate an instruction + // // label line or a function header + // // args: + // // ea_t ea - + // // const char *colored_name - + // // If returns value <=0, then the kernel should + // // not generate the label + // may_show_sreg, // The kernel wants to display the segment registers + // // in the messages window. + // // arg - ea_t current_ea + // // if this function returns 0 + // // then the kernel will not show + // // the segment registers. + // // (assuming that the module have done it) + // coagulate, // Try to define some unexplored bytes + // // This notification will be called if the + // // kernel tried all possibilities and could + // // not find anything more useful than to + // // convert to array of bytes. + // // The module can help the kernel and convert + // // the bytes into something more useful. + // // arg: + // // ea_t start_ea + // // returns: number of converted bytes + 1 + // auto_empty, // Info: all analysis queues are empty + // // args: none + // // returns: none + // // This callback is called once when the + // // initial analysis is finished. If the queue is + // // not empty upon the return from this callback, + // // it will be called later again. + // // See also auto_empty_finally. + // auto_queue_empty, // One analysis queue is empty + // // args: atype_t type + // // returns: 1-yes, keep the queue empty + // // <=0-no, the queue is not empty anymore + // // This callback can be called many times, so + // // only the autoMark() functions can be used from it + // // (other functions may work but it is not tested) + // func_bounds, // find_func_bounds() finished its work + // // The module may fine tune the function bounds + // // args: int *possible_return_code + // // func_t *pfn + // // ea_t max_func_end_ea (from the kernel's point of view) + // // returns: none + // is_jump_func, // is the function a trivial "jump" function? + // // args: func_t *pfn + // // ea_t *jump_target + // // ea_t *func_pointer + // // returns: 0-no, 1-don't know, 2-yes, see jump_target + // // and func_pointer + // gen_regvar_def, // generate register variable definition line + // // args: regvar_t *v + // // returns: 0-ok + // setsgr, // The kernel has changed a segment register value + // // args: ea_t startEA + // // ea_t endEA + // // int regnum + // // sel_t value + // // sel_t old_value + // // uchar tag (SR_... values) + // // returns: 1-ok, 0-error + // set_compiler, // The kernel has changed the compiler information + // // (inf.cc structure) + // is_basic_block_end, // Is the current instruction end of a basic block? + // // This function should be defined for processors + // // with delayed jump slots. The current instruction + // // is stored in 'cmd' + // // args: bool call_insn_stops_block + // // returns: 1-unknown, 0-no, 2-yes + // reglink, // IBM PC only, ignore it + // get_vxd_name, // IBM PC only, ignore it + // // Get Vxd function name + // // args: int vxdnum + // // int funcnum + // // char *outbuf + // // returns: nothing + // + // + // moving_segm, // May the kernel move the segment? + // // args: segment_t - segment to move + // // ea_t to - new segment start address + // // returns: 1-yes, <=0-the kernel should stop + // move_segm, // A segment is moved + // // Fix processor dependent address sensitive information + // // args: ea_t from - old segment address + // // segment_t* - moved segment + // // returns: nothing + // + // + // get_stkvar_scale_factor,// Should stack variable references be multiplied by + // // a coefficient before being used in the stack frame? + // // Currently used by TMS320C55 because the references into + // // the stack should be multiplied by 2 + // // Returns: scaling factor + // // Note: PR_SCALE_STKVARS should be set to use this callback + // + // create_flat_group, // Create special segment representing the flat group + // // (to use for PC mainly) + // // args - ea_t image_base, int bitness, sel_t dataseg_sel + // + // kernel_config_loaded, // This callback is called when ida.cfg is parsed + // // args - none, returns - nothing + // + // might_change_sp, // Does the instruction at 'ea' modify the stack pointer? + // // args: ea_t ea + // // returns: 1-yes, 0-false + // // (not used yet) + // + // is_alloca_probe, // Does the function at 'ea' behave as __alloca_probe? + // // args: ea_t ea + // // returns: 2-yes, 1-false + // + // out_3byte, // Generate text representation of 3byte data + // // init_out_buffer() is called before this function + // // and all Out... function can be used. + // // uFlag contains the flags. + // // This callback might be implemented by the processor + // // module to generate custom representation of 3byte data. + // // args: + // // ea_t dataea - address of the data item + // // uint32 value - value to output + // // bool analyze_only - only create xrefs if necessary + // // do not generate text representation + // // returns: 2-yes, 1-false + // + // get_reg_name, // Generate text representation of a register + // // int reg - internal register number as defined in the processor module + // // size_t width - register width in bytes + // // char *buf - output buffer + // // size_t bufsize - size of output buffer + // // int reghi - if not -1 then this function will return the register pair + // // returns: -1 if error, strlen(buf)+2 otherwise + // // Most processor modules do not need to implement this callback + // // It is useful only if ph.regNames[reg] does not provide + // // the correct register names + // // save its local data + // out_src_file_lnnum, // Callback: generate analog of + // // #line "file.c" 123 + // // directive. + // // const char *file - source file (may be NULL) + // // size_t lnnum - line number + // // returns: 2-directive has been generated + // get_autocmt, // Callback: get dynamic auto comment + // // Will be called if the autocomments are enabled + // // and the comment retrieved from ida.int starts with + // // '$!'. 'cmd' is contains valid info. + // // char *buf - output buffer + // // size_t bufsize - output buffer size + // // returns: 2-new comment has been generated + // // 1-callback has not been handled + // // the buffer must not be changed in this case + // is_insn_table_jump, // Callback: determine if instruction is a table jump or call + // // If CF_JUMP bit can not describe all kinds of table + // // jumps, please define this callback. + // // It will be called for insns with CF_JUMP bit set. + // // input: cmd structure contains the current instruction + // // returns: 1-yes, 0-no + // auto_empty_finally, // Info: all analysis queues are empty definitively + // // args: none + // // returns: none + // // This callback is called only once. + // // See also auto_empty. + // loader_finished, // Event: external file loader finished its work + // // linput_t *li + // // uint16 neflags + // // const char *filetypename + // // Use this event to augment the existing loader functionality + // loader_elf_machine, // Event: ELF loader machine type checkpoint + // // linput_t *li + // // int machine_type + // // const char **p_procname + // // proc_def **p_pd (see ldr\elf.h) + // // set_elf_reloc_t *set_reloc + // // A plugin check the machine_type. If it is the desired one, + // // the the plugin fills p_procname with the processor name. + // // p_pd is used to handle relocations, otherwise can be left untouched + // // set_reloc can be later used by the plugin to specify relocations + // // returns: e_machine value (if it is different from the + // // original e_machine value, procname and p_pd will be ignored + // // and the new value will be used) + // // This event occurs for each loaded ELF file + // is_indirect_jump, // Callback: determine if instruction is an indrect jump + // // If CF_JUMP bit can not describe all jump types + // // jumps, please define this callback. + // // input: cmd structure contains the current instruction + // // returns: 1-use CF_JUMP, 2-no, 3-yes + // verify_noreturn, // The kernel wants to set 'noreturn' flags for a function + // // func_t *pfn + // // Returns: 1-ok, any other value-do not set 'noreturn' flag + // verify_sp, // All function instructions have been analyzed + // // Now the processor module can analyze the stack pointer + // // for the whole function + // // input: func_t *pfn + // // Returns: 1-ok, 0-bad stack pointer + // treat_hindering_item, // An item hinders creation of another item + // // args: ea_t hindering_item_ea + // // flags_t new_item_flags (0 for code) + // // ea_t new_item_ea + // // asize_t new_item_length + // // Returns: 1-no reaction, <=0-the kernel may delete the hindering item + // str2reg, // Convert a register name to a register number + // // args: const char *regname + // // Returns: register number + 2 + // // The register number is the register index in the regNames array + // // Most processor modules do not need to implement this callback + // // It is useful only if ph.regNames[reg] does not provide + // // the correct register names + // create_switch_xrefs, // Create xrefs for a custom jump table + // // in: ea_t jumpea; - address of the jump insn + // // switch_info_ex_t *; - switch information + // // returns: must return 2 + // calc_switch_cases, // Calculate case values and targets for a custom jump table + // // in: ea_t insn_ea - address of the 'indirect jump' instruction + // // switch_info_ex_t *si - switch information + // // casevec_t *casevec - vector of case values... + // // evec_t *targets - ...and corresponding target addresses + // // casevec and targets may be NULL + // // returns: 2-ok, 1-failed + // determined_main, // The main() function has been determined + // // in: ea_t main - address of the main() function + // // returns: none + // preprocess_chart, // gui has retrieved a function flow chart + // // in: qflow_chart_t *fc + // // returns: none + // // Plugins may modify the flow chart in this callback + // get_bg_color, // Get item background color + // // in: ea_t ea, bgcolor_t *color + // // Returns: 1-not implemented, 2-color set + // // Plugins can hook this callback to color disassembly lines + // // dynamically + // get_operand_string, // Request text string for operand (cli, java, ...) + // // args: int opnum + // // char *buf + // // size_t buflen + // // (cmd structure must contain info for the desired insn) + // // opnum is the operand number; -1 means any string operand + // // returns: 1 - no string (or empty string) + // // >1 - original string length with terminating zero + // + // // the following 5 events are very low level + // // take care of possible recursion + // add_cref, // a code reference is being created + // // args: ea_t from, ea_t to, cref_t type + // // returns: <0 - cancel cref creation + // add_dref, // a data reference is being created + // // args: ea_t from, ea_t to, dref_t type + // // returns: <0 - cancel dref creation + // del_cref, // a code reference is being deleted + // // args: ea_t from, ea_t to, bool expand + // // returns: <0 - cancel cref deletion + // del_dref, // a data reference is being deleted + // // args: ea_t from, ea_t to + // // returns: <0 - cancel dref deletion + // coagulate_dref, // data reference is being analyzed + // // args: ea_t from, ea_t to, bool may_define, ea_t *code_ea + // // plugin may correct code_ea (e.g. for thumb mode refs, we clear the last bit) + // // returns: <0 - cancel dref analysis + // custom_fixup, // mutipurpose notification for FIXUP_CUSTOM + // // args: cust_fix oper, ea_t ea, const fixup_data_t*, ... (see cust_fix) + // // returns: 1 - no accepted (fixup ignored by ida) + // // >1 - accepted (see cust_fix) + // off_preproc, // called from get_offset_expr, when refinfo_t + // // contain flag REFINFO_PREPROC. Normally this + // // notification used in a combination with custom_fixup + // // args: ea_t ea, int numop, ea_t* opval, const refinfo_t* ri, + // // char* buf, size_t bufsize, ea_t* target, + // // ea_t* fullvalue, ea_t from, int getn_flags + // // returns: 2 - buf filled as simple expression + // // 3 - buf filled as complex expression + // // 4 - apply standard processing (with - possible - changed values) + // // others - can't convert to offset expression + // + // set_proc_options, // called if the user specified an option string in the command line: + // // -p: + // // can be used for e.g. setting a processor subtype + // // also called if option string is passed to set_processor_type() + // // and IDC's SetProcessorType() + // // args: const char * options + // // returns: <0 - bad option string + // + } + } + catch (Swig::DirectorException &e) + { + msg("Exception in IDP Hook function: %s\n", e.getMessage()); + if ( PyErr_Occurred() ) + PyErr_Print(); + } + return ret; +} + +//--------------------------------------------------------------------------- +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); + +#ifdef NO_OBSOLETE_FUNCS + case idb_event::enum_member_created: +#else + case idb_event::enum_const_created: +#endif + id = va_arg(va, enum_t); + cid = va_arg(va, const_t); + return proxy->enum_member_created(id, cid); + +#ifdef NO_OBSOLETE_FUNCS + case idb_event::enum_member_deleted: +#else + case idb_event::enum_const_deleted: +#endif + 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); + ea = va_arg(va, ea_t); + return proxy->struc_member_deleted(sptr, member_id, ea); + + 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 &e) + { + msg("Exception in IDB Hook function: %s\n", e.getMessage()); + if (PyErr_Occurred()) + { + PyErr_Print(); + } + } + return 0; +} + +//------------------------------------------------------------------------- +static const regval_t *idaapi _py_getreg( + const char *name, + const regval_t *regvals) +{ + for ( int i=dbg->registers_size - 1; i >= 0; i-- ) + { + if ( stricmp(name, dbg->registers[i].name) == 0 ) + return ®vals[i]; + } + static regval_t rv; + return &rv; +} + +//------------------------------------------------------------------------- +// +//------------------------------------------------------------------------- + +#endif diff --git a/pywraps/py_kernwin.hpp b/pywraps/py_kernwin.hpp new file mode 100644 index 0000000..754b482 --- /dev/null +++ b/pywraps/py_kernwin.hpp @@ -0,0 +1,902 @@ +#ifndef __PY_KERNWIN__ +#define __PY_KERNWIN__ + +//------------------------------------------------------------------------ +// +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +/* +# +def register_timer(interval, callback): + """ + Register a timer + + @param interval: Interval in milliseconds + @param callback: A Python callable that takes no parameters and returns an integer. + The callback may return: + -1 : to unregister the timer + >= 0 : the new or same timer interval + @return: None or a timer object + """ + pass +# +*/ +static PyObject *py_register_timer(int interval, PyObject *py_callback) +{ + if ( py_callback == NULL || !PyCallable_Check(py_callback) ) + Py_RETURN_NONE; + + // An inner class hosting the callback method + struct tmr_t + { + static int idaapi callback(void *ud) + { + py_timer_ctx_t *ctx = (py_timer_ctx_t *)ud; + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallFunctionObjArgs(ctx->pycallback, NULL); + int ret = py_result == NULL ? -1 : PyLong_AsLong(py_result); + Py_XDECREF(py_result); + PYW_GIL_RELEASE; + + // Timer has been unregistered? + if ( ret == -1 ) + { + // Fee the context + Py_DECREF(ctx->pycallback); + delete ctx; + } + return ret; + }; + }; + + py_timer_ctx_t *ctx = new py_timer_ctx_t(); + ctx->pycallback = py_callback; + Py_INCREF(py_callback); + ctx->timer_id = register_timer( + interval, + tmr_t::callback, + ctx); + + if ( ctx->timer_id == NULL ) + { + Py_DECREF(py_callback); + delete ctx; + Py_RETURN_NONE; + } + return PyCObject_FromVoidPtr(ctx, NULL); +} + +//------------------------------------------------------------------------ +/* +# +def unregister_timer(timer_obj): + """ + Unregister a timer + + @param timer_obj: a timer object previously returned by a register_timer() + @return: Boolean + @note: After the timer has been deleted, the timer_obj will become invalid. + """ + pass +# +*/ +static PyObject *py_unregister_timer(PyObject *py_timerctx) +{ + if ( py_timerctx == NULL || !PyCObject_Check(py_timerctx) ) + Py_RETURN_FALSE; + + py_timer_ctx_t *ctx = (py_timer_ctx_t *) PyCObject_AsVoidPtr(py_timerctx); + if ( !unregister_timer(ctx->timer_id) ) + Py_RETURN_FALSE; + + Py_DECREF(ctx->pycallback); + delete ctx; + + Py_RETURN_TRUE; +} + +//------------------------------------------------------------------------ +/* +# +def choose_idasgn(): + """ + Opens the signature chooser + + @return: None or the selected signature name + """ + pass +# +*/ +static PyObject *py_choose_idasgn() +{ + char *name = choose_idasgn(); + if ( name == NULL ) + { + Py_RETURN_NONE; + } + else + { + PyObject *py_str = PyString_FromString(name); + qfree(name); + return py_str; + } +} + +//------------------------------------------------------------------------ +/* +# +def get_highlighted_identifier(flags = 0): + """ + Returns the currently highlighted identifier + + @param flags: reserved (pass 0) + @return: None or the highlighted identifier + """ + pass +# +*/ +static PyObject *py_get_highlighted_identifier(int flags = 0) +{ + char buf[MAXSTR]; + bool ok = get_highlighted_identifier(buf, sizeof(buf), flags); + if ( !ok ) + Py_RETURN_NONE; + else + return PyString_FromString(buf); +} + +//------------------------------------------------------------------------ +static int py_load_custom_icon_fn(const char *filename) +{ + return load_custom_icon(filename); +} + +//------------------------------------------------------------------------ +static int py_load_custom_icon_data(PyObject *data, const char *format) +{ + Py_ssize_t len; + char *s; + if ( PyString_AsStringAndSize(data, &s, &len) == -1 ) + return 0; + else + return load_custom_icon(s, len, format); +} + +//------------------------------------------------------------------------ +/* +# +def free_custom_icon(icon_id): + """ + Frees an icon loaded with load_custom_icon() + """ + pass +# +*/ + +//------------------------------------------------------------------------ +/* +# +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, "%s", 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 process_ui_action(name, flags): + """ + Invokes an IDA UI action by name + + @param name: action name + @param flags: Reserved. Must be zero + @return: Boolean + """ + pass +# +*/ +static bool py_process_ui_action(const char *name, int flags) +{ + return process_ui_action(name, flags, NULL); +} + +//------------------------------------------------------------------------ +/* +# +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 del_hotkey(ctx): + """ + Deletes a previously registered function hotkey + + @param ctx: Hotkey context previously returned by add_hotkey() + + @return: Boolean. + """ + pass +# +*/ +bool py_del_hotkey(PyObject *pyctx) +{ + if ( !PyCObject_Check(pyctx) ) + return false; + + py_idchotkey_ctx_t *ctx = (py_idchotkey_ctx_t *) PyCObject_AsVoidPtr(pyctx); + if ( !del_idc_hotkey(ctx->hotkey.c_str()) ) + return false; + + Py_DECREF(ctx->pyfunc); + delete ctx; + return true; +} + +//------------------------------------------------------------------------ +/* +# +def add_hotkey(hotkey, pyfunc): + """ + Associates a function call with a hotkey. + Callable pyfunc will be called each time the hotkey is pressed + + @param hotkey: The hotkey + @param pyfunc: Callable + + @return: Context object on success or None on failure. + """ + pass +# +*/ +PyObject *py_add_hotkey(const char *hotkey, PyObject *pyfunc) +{ + // Make sure a callable was passed + if ( !PyCallable_Check(pyfunc) ) + return NULL; + + // Form the function name + qstring idc_func_name; + idc_func_name.sprnt("py_hotkeycb_%p", pyfunc); + + // Can add the hotkey? + if ( add_idc_hotkey(hotkey, idc_func_name.c_str()) == IDCHK_OK ) do + { + // Generate global variable name + qstring idc_gvarname; + idc_gvarname.sprnt("_g_pyhotkey_ref_%p", pyfunc); + + // Now add the global variable + idc_value_t *gvar = add_idc_gvar(idc_gvarname.c_str()); + if ( gvar == NULL ) + break; + + // The function body will call a registered IDC function that + // will take a global variable that wraps a PyCallable as a pvoid + qstring idc_func; + idc_func.sprnt("static %s() { %s(%s); }", + idc_func_name.c_str(), + S_PYINVOKE0, + idc_gvarname.c_str()); + + // Compile the IDC condition + char errbuf[MAXSTR]; + if ( !CompileLineEx(idc_func.c_str(), errbuf, sizeof(errbuf)) ) + break; + + // Create new context + // Define context + py_idchotkey_ctx_t *ctx = new py_idchotkey_ctx_t(); + + // Remember the hotkey + ctx->hotkey = hotkey; + + // Take reference to the callable + ctx->pyfunc = pyfunc; + Py_INCREF(pyfunc); + + // Bind IDC variable w/ the PyCallable + gvar->set_pvoid(pyfunc); + + // Return the context + return PyCObject_FromVoidPtr(ctx, NULL); + } while (false); + + // Cleanup + del_idc_hotkey(hotkey); + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------ +/* +# +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 + @param 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 +# +*/ +bool idaapi py_menu_item_callback(void *userdata); +static PyObject *py_add_menu_item( + const char *menupath, + const char *name, + const char *hotkey, + int flags, + PyObject *pyfunc, + PyObject *args) +{ + bool no_args; + + // No slash in the menu path? + const char *p = strrchr(menupath, '/'); + if ( p == NULL ) + Py_RETURN_NONE; + + if ( args == Py_None ) + { + no_args = true; + args = PyTuple_New(0); + if ( args == NULL ) + return NULL; + } + else if ( !PyTuple_Check(args) ) + { + PyErr_SetString(PyExc_TypeError, "args must be a tuple or None"); + return NULL; + } + else + { + no_args = false; + } + + // Form a tuple holding the function to be called and its arguments + PyObject *cb_data = Py_BuildValue("(OO)", pyfunc, args); + + // If we created an empty tuple, then we must free it + if ( no_args ) + Py_DECREF(args); + + // Add the menu item + 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; + } + // Create a context (for the delete_menu_item()) + py_add_del_menu_item_ctx *ctx = new py_add_del_menu_item_ctx(); + + // Form the complete menu path + ctx->menupath.append(menupath, p - menupath + 1); + ctx->menupath.append(name); + // Save callback data + ctx->cb_data = cb_data; + + // Return context to user + 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""" + +MFF_NOWAIT = 0x0004 +"""Do not wait for the request to be executed. +he caller should ensure that the request is not +destroyed until the execution completes. +if not, the request will be ignored. +the return code of execute_sync() is meaningless +in this case. +This flag can be used to delay the code execution +until the next UI loop run even from the main thread""" + +def execute_sync(callable, reqf) + """ + Executes a function in the context of the main thread. + If the current thread not the main thread, then the call is queued and + executed afterwards. + + @note: The Python version of execute_sync() cannot be called from a different thread + for the time being. + @param callable: A python callable object + @param reqf: one of MFF_ flags + @return: -1 or the return value of the callable + """ + pass +# +*/ +//------------------------------------------------------------------------ +static int py_execute_sync(PyObject *py_callable, int reqf) +{ + // Not callable? + if ( !PyCallable_Check(py_callable) ) + return -1; + + struct py_exec_request_t: exec_request_t + { + PyObject *py_callable; + bool no_wait; + virtual int idaapi execute() + { + PYW_GIL_ENSURE; + 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); + PYW_GIL_RELEASE; + + // Free this request + if ( no_wait ) + delete this; + + return r; + } + py_exec_request_t(PyObject *pyc, bool no_wait): + py_callable(pyc), no_wait(no_wait) + { + // Take reference to the callable + Py_INCREF(py_callable); + } + virtual ~py_exec_request_t() + { + // Release callable + Py_XDECREF(py_callable); + } + }; + + bool no_wait = (reqf & MFF_NOWAIT) != 0; + + // Allocate a request + py_exec_request_t *req = new py_exec_request_t(py_callable, no_wait); + + // Execute it + int r = execute_sync(*req, reqf); + + // Delete only if NOWAIT was not specified + // (Otherwise the request will delete itself) + if ( !no_wait ) + delete req; + + return r; +} + +//------------------------------------------------------------------------ +/* +# + +def execute_ui_requests(callable_list) + """ + Inserts a list of callables into the UI message processing queue. + When the UI is ready it will call one callable. + A callable can request to be called more than once if it returns True. + + @param callable_list: A list of python callable objects. + @note: A callable should return True if it wants to be called more than once. + @return: Boolean. False if the list contains a non callabale item + """ + pass +# +*/ +static bool py_execute_ui_requests(PyObject *py_list) +{ + struct py_ui_request_t: public ui_request_t + { + private: + ppyobject_vec_t py_callables; + size_t py_callable_idx; + + static int idaapi s_py_list_walk_cb( + PyObject *py_item, + Py_ssize_t index, + void *ud) + { + // Not callable? Terminate iteration + if ( !PyCallable_Check(py_item) ) + return CIP_FAILED; + + // Append this callable and increment its reference + py_ui_request_t *_this = (py_ui_request_t *)ud; + _this->py_callables.push_back(py_item); + Py_INCREF(py_item); + + return CIP_OK; + } + public: + py_ui_request_t(): py_callable_idx(0) + { + } + + virtual bool idaapi run() + { + // Get callable + PyObject *py_callable = py_callables.at(py_callable_idx); + + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallFunctionObjArgs(py_callable, NULL); + bool reschedule = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + PYW_GIL_RELEASE; + + // No rescheduling? Then advance to the next callable + if ( !reschedule ) + ++py_callable_idx; + + // Reschedule this C callback only if there are more callables + return py_callable_idx < py_callables.size(); + } + + // Walk the list and extract all callables + bool init(PyObject *py_list) + { + Py_ssize_t count = pyvar_walk_list( + py_list, + s_py_list_walk_cb, + this); + return count > 0; + } + + virtual idaapi ~py_ui_request_t() + { + // Release all callables + for ( ppyobject_vec_t::const_iterator it=py_callables.begin(); + it != py_callables.end(); + ++it ) + { + Py_XDECREF(*it); + } + } + }; + + py_ui_request_t *req = new py_ui_request_t(); + if ( !req->init(py_list) ) + { + delete req; + return false; + } + execute_ui_requests(req, NULL); + return true; +} + +//------------------------------------------------------------------------ +/* +# +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 +# +*/ + +//------------------------------------------------------------------------ +/* +# +def is_idaq(): + """ + Returns True or False depending if IDAPython is hosted by IDAQ + """ +# +*/ + +//--------------------------------------------------------------------------- +// UI hooks +//--------------------------------------------------------------------------- +int idaapi UI_Callback(void *ud, int notification_code, va_list va); +/* +# +class UI_Hooks(object): + def hook(self): + """ + Creates an UI hook + + @return: Boolean true on success + """ + pass + + def unhook(self): + """ + Removes the UI hook + @return: Boolean true on success + """ + pass + + def preprocess(self, name): + """ + IDA ui is about to handle a user command + + @param name: ui command name + (these names can be looked up in ida[tg]ui.cfg) + @return: 0-ok, nonzero - a plugin has handled the command + """ + pass + + def postprocess(self): + """ + An ida ui command has been handled + + @return: Ignored + """ + pass + + def saving(self): + """ + The kernel is saving the database. + + @return: Ignored + """ + pass + + def saved(self): + """ + The kernel has saved the database. + + @return: Ignored + """ + pass + + def get_ea_hint(self, ea): + """ + The UI wants to display a simple hint for an address in the navigation band + + @param ea: The address + @return: String with the hint or None + """ + pass + + def term(self): + """ + IDA is terminated and the database is already closed. + The UI may close its windows in this callback. + """ + pass + +# +*/ +class UI_Hooks +{ +public: + virtual ~UI_Hooks() + { + } + + bool hook() + { + return hook_to_notification_point(HT_UI, UI_Callback, this); + } + + bool unhook() + { + return unhook_from_notification_point(HT_UI, UI_Callback, this); + } + + virtual int preprocess(const char * /*name*/) + { + return 0; + } + + virtual void postprocess() + { + } + + virtual void saving() + { + } + + virtual void saved() + { + } + + virtual void term() + { + } + + virtual PyObject *get_ea_hint(ea_t /*ea*/) + { + Py_RETURN_NONE; + }; +}; +// + +//--------------------------------------------------------------------------- +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +int idaapi UI_Callback(void *ud, int notification_code, va_list va) +{ + UI_Hooks *proxy = (UI_Hooks *)ud; + int ret = 0; + try + { + switch (notification_code) + { + case ui_preprocess: + { + const char *name = va_arg(va, const char *); + return proxy->preprocess(name); + } + + case ui_postprocess: + proxy->postprocess(); + break; + + case ui_saving: + proxy->saving(); + break; + + case ui_saved: + proxy->saved(); + break; + + case ui_term: + proxy->term(); + break; + + case ui_get_ea_hint: + { + ea_t ea = va_arg(va, ea_t); + char *buf = va_arg(va, char *); + size_t sz = va_arg(va, size_t); + char *_buf; + Py_ssize_t _len; + + PyObject *py_str = proxy->get_ea_hint(ea); + if ( py_str != NULL + && PyString_Check(py_str) + && PyString_AsStringAndSize(py_str, &_buf, &_len) != - 1 ) + { + qstrncpy(buf, _buf, qmin(_len, sz)); + ret = 1; + } + break; + } + } + } + catch (Swig::DirectorException &e) + { + msg("Exception in UI Hook function: %s\n", e.getMessage()); + if ( PyErr_Occurred() ) + PyErr_Print(); + } + return ret; +} + +//------------------------------------------------------------------------ +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 + PYW_GIL_ENSURE; + PyObject *result = PyEval_CallObject(func, args); + PYW_GIL_RELEASE; + + // We cannot raise an exception in the callback, just print it. + if ( result == NULL ) + { + PyErr_Print(); + return false; + } + + bool ret = PyObject_IsTrue(result) != 0; + Py_DECREF(result); + return ret; +} +// + +#endif \ No newline at end of file diff --git a/pywraps/py_kernwin.py b/pywraps/py_kernwin.py new file mode 100644 index 0000000..ec8c1dd --- /dev/null +++ b/pywraps/py_kernwin.py @@ -0,0 +1,110 @@ +# ----------------------------------------------------------------------- +# Standalone and testing code +import sys +try: + import pywraps + pywraps_there = True + print "Using pywraps" +except: + pywraps_there = False + print "Not using pywraps" + +try: + import _idaapi +except: + print "Please try me from inside IDA" + sys.exit(0) + +if pywraps_there: + _idaapi.execute_sync = pywraps.py_execute_sync + _idaapi.add_hotkey = pywraps.py_add_hotkey + _idaapi.del_hotkey = pywraps.py_del_hotkey + +# ----------------------------------------------------------------------- +# +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 + +# ---------------------------------------------------------------------- +def load_custom_icon(file_name=None, data=None, format=None): + """ + Loads a custom icon and returns an identifier that can be used with other APIs + + If file_name is passed then the other two arguments are ignored. + + @param file_name: The icon file name + @param data: The icon data + @param format: The icon data format + + @return: Icon id or 0 on failure. + Use free_custom_icon() to free it + """ + if file_name is not None: + return _idaapi.py_load_custom_icon_fn(file_name) + elif not (data is None and format is None): + return _idaapi.py_load_custom_icon_data(data, format) + else: + return 0 + +# ---------------------------------------------------------------------- +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 + +# + +# ---------------------------------------------------------------------- +from threading import Thread +import time + +# ---------------------------------------------------------------------- +def myfunction(cnt): + i = 1 + while i <= cnt: + print "i=", i + i += 1 + time.sleep(1) + + print "done!" + +def test_thread(): + t = Thread(target=myfunction, args=(2,)) + + t.start() + t.join() + +# ---------------------------------------------------------------------- +def hotkey_func1(): + print "Hello from hotkey handler in Python!" diff --git a/pywraps/py_lines.hpp b/pywraps/py_lines.hpp new file mode 100644 index 0000000..8063010 --- /dev/null +++ b/pywraps/py_lines.hpp @@ -0,0 +1,211 @@ +#ifndef __PYWRAPS__LINES__ +#define __PYWRAPS__LINES__ + +// +//------------------------------------------------------------------------ +static PyObject *py_get_user_defined_prefix = NULL; +static void idaapi s_py_get_user_defined_prefix( + ea_t ea, + int lnnum, + int indent, + const char *line, + char *buf, + size_t bufsize) +{ + PyObject *py_ret = PyObject_CallFunction( + py_get_user_defined_prefix, + PY_FMT64 "iis" PY_FMT64, + ea, lnnum, indent, line, bufsize); + + // Error? Display it + // No error? Copy the buffer + if ( !PyW_ShowCbErr("py_get_user_defined_prefix") ) + { + Py_ssize_t py_len; + char *py_str; + if ( PyString_AsStringAndSize(py_ret, &py_str, &py_len) != -1 ) + { + memcpy(buf, py_str, qmin(bufsize, py_len)); + if ( py_len < bufsize ) + buf[py_len] = '\0'; + } + } + Py_XDECREF(py_ret); +} +// + +//------------------------------------------------------------------------ + +// + +//------------------------------------------------------------------------ +/* +# +def set_user_defined_prefix(width, callback): + """ + User-defined line-prefixes are displayed just after the autogenerated + line prefixes. In order to use them, the plugin should call the + following function to specify its width and contents. + @param width: the width of the user-defined prefix + @param callback: a get_user_defined_prefix callback to get the contents of the prefix. + Its arguments: + ea - linear address + lnnum - line number + indent - indent of the line contents (-1 means the default instruction) + indent and is used for instruction itself. see explanations for printf_line() + line - the line to be generated. the line usually contains color tags this argument + can be examined to decide whether to generated the prefix + bufsize- the maximum allowed size of the output buffer + It returns a buffer of size < bufsize + + In order to remove the callback before unloading the plugin, specify the width = 0 or the callback = None + """ + pass +# +*/ +static PyObject *py_set_user_defined_prefix(size_t width, PyObject *pycb) +{ + if ( width == 0 || pycb == Py_None ) + { + // Release old callback reference + Py_XDECREF(py_get_user_defined_prefix); + + // ...and clear it + py_get_user_defined_prefix = NULL; + + // Uninstall user defind prefix + set_user_defined_prefix(0, NULL); + } + else if ( PyCallable_Check(pycb) ) + { + // Release old callback reference + Py_XDECREF(py_get_user_defined_prefix); + + // Copy new callback and hold a reference + py_get_user_defined_prefix = pycb; + Py_INCREF(py_get_user_defined_prefix); + + set_user_defined_prefix(width, s_py_get_user_defined_prefix); + } + else + { + Py_RETURN_FALSE; + } + Py_RETURN_TRUE; +} + +//------------------------------------------------------------------------- +/* +# +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 +#endif diff --git a/pywraps/py_lines.py b/pywraps/py_lines.py new file mode 100644 index 0000000..3c72541 --- /dev/null +++ b/pywraps/py_lines.py @@ -0,0 +1,34 @@ +# + +# ---------------- 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 + +# diff --git a/pywraps/py_linput.hpp b/pywraps/py_linput.hpp new file mode 100644 index 0000000..12a5fda --- /dev/null +++ b/pywraps/py_linput.hpp @@ -0,0 +1,361 @@ +#ifndef __PY_IDA_LINPUT__ +#define __PY_IDA_LINPUT__ + +//-------------------------------------------------------------------------- +// +/* +# +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: + linput_t *li; + int own; + qstring fn; + enum + { + OWN_NONE = 0, // li not created yet + OWN_CREATE = 1, // Owns li because we created it + OWN_FROM_LI = 2, // No ownership we borrowed the li from another class + OWN_FROM_FP = 3, // We got an li instance from an fp instance, we have to unmake_linput() on Close + }; + + //-------------------------------------------------------------------------- + void _from_cobject(PyObject *pycobject) + { + this->set_linput((linput_t *)PyCObject_AsVoidPtr(pycobject)); + } + + //-------------------------------------------------------------------------- + void assign(const loader_input_t &rhs) + { + fn = rhs.fn; + li = rhs.li; + own = OWN_FROM_LI; + } + + //-------------------------------------------------------------------------- + loader_input_t(const loader_input_t &rhs) + { + assign(rhs); + } +public: + // Special attribute that tells the pyvar_to_idcvar how to convert this + // class from and to IDC. The value of this variable must be set to two + int __idc_cvt_id__; + //-------------------------------------------------------------------------- + loader_input_t(PyObject *pycobject = NULL): li(NULL), own(OWN_NONE), __idc_cvt_id__(PY_ICID_OPAQUE) + { + if ( pycobject != NULL && PyCObject_Check(pycobject) ) + _from_cobject(pycobject); + } + + //-------------------------------------------------------------------------- + void close() + { + if ( li == NULL ) + return; + + if ( own == OWN_CREATE ) + close_linput(li); + else if ( own == OWN_FROM_FP ) + unmake_linput(li); + + li = NULL; + own = OWN_NONE; + } + + //-------------------------------------------------------------------------- + ~loader_input_t() + { + close(); + } + + //-------------------------------------------------------------------------- + bool open(const char *filename, bool remote = false) + { + close(); + li = open_linput(filename, remote); + if ( li == NULL ) + return false; + + // Save file name + fn = filename; + own = OWN_CREATE; + return true; + } + + //-------------------------------------------------------------------------- + void set_linput(linput_t *linput) + { + close(); + own = OWN_FROM_LI; + li = linput; + fn.sprnt("", linput); + } + + //-------------------------------------------------------------------------- + static loader_input_t *from_linput(linput_t *linput) + { + loader_input_t *l = new loader_input_t(); + l->set_linput(linput); + return l; + } + + //-------------------------------------------------------------------------- + // This method can be used to pass a linput_t* from C code + static loader_input_t *from_cobject(PyObject *pycobject) + { + if ( !PyCObject_Check(pycobject) ) + return NULL; + loader_input_t *l = new loader_input_t(); + l->_from_cobject(pycobject); + return l; + } + + //-------------------------------------------------------------------------- + static loader_input_t *from_fp(FILE *fp) + { + linput_t *fp_li = make_linput(fp); + if ( fp_li == NULL ) + return NULL; + + loader_input_t *l = new loader_input_t(); + l->own = OWN_FROM_FP; + l->fn.sprnt("", fp); + l->li = fp_li; + return l; + } + + //-------------------------------------------------------------------------- + linput_t *get_linput() + { + return li; + } + + //-------------------------------------------------------------------------- + bool open_memory(ea_t start, asize_t size = 0) + { + linput_t *l = create_memory_linput(start, size); + if ( l == NULL ) + return false; + close(); + li = l; + fn = ""; + own = OWN_CREATE; + return true; + } + + //-------------------------------------------------------------------------- + int32 seek(int32 pos, int whence = SEEK_SET) + { + return qlseek(li, pos, whence); + } + + //-------------------------------------------------------------------------- + int32 tell() + { + return qltell(li); + } + + //-------------------------------------------------------------------------- + PyObject *getz(size_t sz, int32 fpos = -1) + { + do + { + char *buf = (char *) malloc(sz + 5); + if ( buf == NULL ) + break; + qlgetz(li, fpos, buf, sz); + PyObject *ret = PyString_FromString(buf); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *gets(size_t len) + { + do + { + char *buf = (char *) malloc(len + 5); + if ( buf == NULL ) + break; + if ( qlgets(buf, len, li) == NULL ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromString(buf); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *read(size_t size) + { + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + ssize_t r = qlread(li, buf, size); + if ( r == -1 ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + bool opened() + { + return li != NULL; + } + + //-------------------------------------------------------------------------- + PyObject *readbytes(size_t size, bool big_endian) + { + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + int r = lreadbytes(li, buf, size, big_endian); + if ( r == -1 ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + int file2base(int32 pos, ea_t ea1, ea_t ea2, int patchable) + { + return ::file2base(li, pos, ea1, ea2, patchable); + } + + //-------------------------------------------------------------------------- + int32 size() + { + return qlsize(li); + } + + //-------------------------------------------------------------------------- + PyObject *filename() + { + return PyString_FromString(fn.c_str()); + } + + //-------------------------------------------------------------------------- + PyObject *get_char() + { + int ch = qlgetc(li); + if ( ch == EOF ) + Py_RETURN_NONE; + return Py_BuildValue("c", ch); + } +}; +// + +#endif diff --git a/pywraps/py_loader.hpp b/pywraps/py_loader.hpp new file mode 100644 index 0000000..b122487 --- /dev/null +++ b/pywraps/py_loader.hpp @@ -0,0 +1,78 @@ +#ifndef __PY_LOADER___ +#define __PY_LOADER___ + +//------------------------------------------------------------------------ +// + +//------------------------------------------------------------------------ +/* +# +def mem2base(mem, ea, fpos): + """ + Load database from the memory. + @param mem: the buffer + @param ea: start linear addresses + @param fpos: position in the input file the data is taken from. + if == -1, then no file position correspond to the data. + @return: + - Returns zero if the passed buffer was not a string + - Otherwise 1 is returned + """ + pass +# +*/ +static int py_mem2base(PyObject *py_mem, ea_t ea, long fpos = -1) +{ + Py_ssize_t len; + char *buf; + if ( PyString_AsStringAndSize(py_mem, &buf, &len) == -1 ) + return 0; + + return mem2base((void *)buf, ea, ea+len, fpos); +} + +//------------------------------------------------------------------------ +/* +# +def load_plugin(name): + """ + Loads a plugin + @return: + - None if plugin could not be loaded + - An opaque object representing the loaded plugin + """ + pass +# +*/ +static PyObject *py_load_plugin(const char *name) +{ + plugin_t *r = load_plugin(name); + if ( r == NULL ) + Py_RETURN_NONE; + else + return PyCObject_FromVoidPtr(r, NULL); +} + +//------------------------------------------------------------------------ +/* +# +def run_plugin(plg): + """ + Runs a plugin + @param plg: A plugin object (returned by load_plugin()) + @return: Boolean + """ + pass +# +*/ +static bool py_run_plugin(PyObject *plg, int arg) +{ + if ( !PyCObject_Check(plg) ) + return false; + else + return run_plugin((plugin_t *)PyCObject_AsVoidPtr(plg), arg); +} + +// + +#endif diff --git a/pywraps/py_nalt.hpp b/pywraps/py_nalt.hpp new file mode 100644 index 0000000..d417e9d --- /dev/null +++ b/pywraps/py_nalt.hpp @@ -0,0 +1,486 @@ +#ifndef __PY_IDA_NALT__ +#define __PY_IDA_NALT__ + +// + +//------------------------------------------------------------------------- +// callback for enumerating imports +// ea: import address +// name: import name (NULL if imported by ordinal) +// ord: import ordinal (0 for imports by name) +// param: user parameter passed to enum_import_names() +// return: 1-ok, 0-stop enumeration +static int idaapi py_import_enum_cb( + ea_t ea, + const char *name, + uval_t ord, + void *param) +{ + // If no name, try to get the name associated with the 'ea'. It may be coming from IDS + char name_buf[MAXSTR]; + if ( name == NULL ) + name = get_true_name(BADADDR, ea, name_buf, sizeof(name_buf)); + + PyObject *py_name; + if ( name == NULL ) + { + py_name = Py_None; + Py_INCREF(Py_None); + } + else + { + py_name = PyString_FromString(name); + } + + PyObject *py_ord = Py_BuildValue(PY_FMT64, pyul_t(ord)); + PyObject *py_ea = Py_BuildValue(PY_FMT64, pyul_t(ea)); + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallFunctionObjArgs( + (PyObject *)param, + py_ea, + py_name, + py_ord, + NULL); + PYW_GIL_RELEASE; + + int r = py_result != NULL && PyObject_IsTrue(py_result) ? 1 : 0; + + Py_DECREF(py_ea); + Py_DECREF(py_name); + Py_DECREF(py_ord); + Py_XDECREF(py_result); + + return r; +} + +//------------------------------------------------------------------------- +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; +} +// + +// + +//------------------------------------------------------------------------- +/* +# +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)) ) + Py_RETURN_NONE; + + return PyString_FromString(buf); +} + +//------------------------------------------------------------------------- +/* +# +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); PyW_GetNumber(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); PyW_GetNumber(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); + PyW_GetNumber(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); + PyW_GetNumber(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); + PyW_GetNumber(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); + PyW_GetNumber(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); + PyW_GetNumber(value, &v); + link->values = (pyul_t)v; +} + +// +// Auto generated - end +// +//------------------------------------------------------------------------- +// + +#endif diff --git a/pywraps/py_nalt.py b/pywraps/py_nalt.py new file mode 100644 index 0000000..c1b1662 --- /dev/null +++ b/pywraps/py_nalt.py @@ -0,0 +1,191 @@ +# +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 self.ncases + + def get_lowcase(self): + return self.ind_lowcase if self.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/pywraps/py_name.hpp b/pywraps/py_name.hpp new file mode 100644 index 0000000..7f802b9 --- /dev/null +++ b/pywraps/py_name.hpp @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------ +// +//------------------------------------------------------------------------ +PyObject *py_get_debug_names(ea_t ea1, ea_t ea2) +{ + // Get debug names + ea_name_vec_t names; + get_debug_names(ea1, ea2, names); + PyObject *dict = Py_BuildValue("{}"); + if (dict == NULL) + return NULL; + + for (ea_name_vec_t::iterator it=names.begin();it!=names.end();++it) + { + PyDict_SetItem(dict, + Py_BuildValue(PY_FMT64, it->ea), + PyString_FromString(it->name.c_str())); + } + return dict; +} +//------------------------------------------------------------------------ +// +//------------------------------------------------------------------------ diff --git a/pywraps/py_name.py b/pywraps/py_name.py new file mode 100644 index 0000000..2f6e01d --- /dev/null +++ b/pywraps/py_name.py @@ -0,0 +1,52 @@ +import bisect + +# + +class NearestName: + """ + Utility class to help find the nearest name in a given ea/name dictionary + """ + def __init__(self, ea_names): + self.update(ea_names) + + + def update(self, ea_names): + """Updates the ea/names map""" + self._names = ea_names + self._addrs = ea_names.keys() + self._addrs.sort() + + + def find(self, ea): + """ + Returns a tupple (ea, name, pos) that is the nearest to the passed ea + If no name is matched then None is returned + """ + pos = bisect.bisect_left(self._addrs, ea) + # no match + if pos >= len(self._addrs): + return None + # exact match? + if self._addrs[pos] != ea: + pos -= 1 # go to previous element + if pos < 0: + return None + return self[pos] + + + def _get_item(self, index): + ea = self._addrs[index] + return (ea, self._names[ea], index) + + + def __iter__(self): + return (self._get_item(index) for index in xrange(0, len(self._addrs))) + + + def __getitem__(self, index): + """Returns the tupple (ea, name, index)""" + if index > len(self._addrs): + raise StopIteration + return self._get_item(index) + +# \ No newline at end of file diff --git a/pywraps/py_notifywhen.hpp b/pywraps/py_notifywhen.hpp new file mode 100644 index 0000000..d11039a --- /dev/null +++ b/pywraps/py_notifywhen.hpp @@ -0,0 +1,307 @@ +#ifndef __PYWRAPS_NOTIFY_WHEN__ +#define __PYWRAPS_NOTIFY_WHEN__ + +//------------------------------------------------------------------------ +// +//------------------------------------------------------------------------ + +//------------------------------------------------------------------------ +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: + { + PYW_GIL_ENSURE; + py_result = PyObject_CallFunctionObjArgs(*it, py_code, NULL); + PYW_GIL_RELEASE; + break; + } + case NW_OPENIDB_SLOT: + { + PyObject *py_old = PyInt_FromLong(old); + PYW_GIL_ENSURE; + py_result = PyObject_CallFunctionObjArgs(*it, py_code, py_old, NULL); + PYW_GIL_RELEASE; + Py_DECREF(py_old); + } + break; + } + Py_DECREF(py_code); + if ( PyW_GetError(&err) || py_result == NULL ) + { + PyErr_Clear(); + 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; +} + +// +//------------------------------------------------------------------------ + +// + +//------------------------------------------------------------------------ +/* +# +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); +} + +// +//------------------------------------------------------------------------ + +#endif diff --git a/pywraps/py_notifywhen.py b/pywraps/py_notifywhen.py new file mode 100644 index 0000000..bb07041 --- /dev/null +++ b/pywraps/py_notifywhen.py @@ -0,0 +1,63 @@ +# ----------------------------------------------------------------------- +# Standalone and testing code +import sys +try: + import pywraps + pywraps_there = True + print "Using pywraps" +except: + pywraps_there = False + print "Not using pywraps" + +try: + import _idaapi +except: + print "Please try me from inside IDA" + sys.exit(0) + +import struct + +if pywraps_there: + _idaapi.notify_when = pywraps.notify_when + +# ----------------------------------------------------------------------- +# +# 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""" + +# +# ----------------------------------------------------------------------- + +def nw_openidb(code, old): + print "Open IDB, old=", old + +def nw_closeidb(code): + print "Close IDB" + +def nw_openclose(code, old = None): + if code == NW_CLOSEIDB: + print "openclose: Close IDB" + elif code == NW_OPENIDB: + print "openclose: Open IDB, old=", old + +def nw_closeida(code): + import ctypes + user32 = ctypes.windll.user32 + user32.MessageBoxA(0, "Close IDA", "Info", 0) + +print "registering nw_openidb->", _idaapi.notify_when(NW_OPENIDB, nw_openidb) +print "registering nw_closeidb->", _idaapi.notify_when(NW_CLOSEIDB, nw_closeidb) +print "registering nw_openclose->", _idaapi.notify_when(NW_OPENIDB|NW_CLOSEIDB, nw_openclose) +print "registering nw_closeida->", _idaapi.notify_when(NW_TERMIDA, nw_closeida) diff --git a/pywraps/py_plgform.hpp b/pywraps/py_plgform.hpp new file mode 100644 index 0000000..1ffcfda --- /dev/null +++ b/pywraps/py_plgform.hpp @@ -0,0 +1,158 @@ +#ifndef __PY_PLGFORM__ +#define __PY_PLGFORM__ + +// +//--------------------------------------------------------------------------- +class plgform_t +{ +private: + PyObject *py_obj; + TForm *form; + + static int idaapi s_callback(void *ud, int notification_code, va_list va) + { + plgform_t *_this = (plgform_t *)ud; + if ( notification_code == ui_tform_visible ) + { + TForm *form = va_arg(va, TForm *); + if ( form == _this->form ) + { + // Qt: QWidget* + // G: HWND + // We wrap and pass as a CObject in the hope that a Python UI framework + // can unwrap a CObject and get the hwnd/widget back + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod( + _this->py_obj, + (char *)S_ON_CREATE, "O", + PyCObject_FromVoidPtr(form, NULL)); + PYW_GIL_RELEASE; + + PyW_ShowCbErr(S_ON_CREATE); + Py_XDECREF(py_result); + } + } + else if ( notification_code == ui_tform_invisible ) + { + TForm *form = va_arg(va, TForm *); + if ( form == _this->form ) + { + PYW_GIL_ENSURE; + PyObject *py_result = PyObject_CallMethod( + _this->py_obj, + (char *)S_ON_CLOSE, "O", + PyCObject_FromVoidPtr(form, NULL)); + PYW_GIL_RELEASE; + + PyW_ShowCbErr(S_ON_CLOSE); + Py_XDECREF(py_result); + + _this->unhook(); + } + } + return 0; + } + + void unhook() + { + unhook_from_notification_point(HT_UI, s_callback, this); + form = NULL; + + // Call DECREF at last, since it may trigger __del__ + Py_XDECREF(py_obj); + } + +public: + plgform_t(): py_obj(NULL), form(NULL) + { + } + + bool show( + PyObject *obj, + const char *caption, + int options) + { + // Already displayed? + TForm *f = find_tform(caption); + if ( f != NULL ) + { + // Our form? + if ( f == form ) + { + // Switch to it + switchto_tform(form, true); + return true; + } + // Fail to create + return false; + } + + // Create a form + form = create_tform(caption, NULL); + if ( form == NULL ) + return false; + + if ( !hook_to_notification_point(HT_UI, s_callback, this) ) + { + form = NULL; + return false; + } + + py_obj = obj; + Py_INCREF(obj); + + if ( is_idaq() ) + options |= FORM_QWIDGET; + + this->form = form; + open_tform(form, options); + return true; + } + + void close(int options = 0) + { + if ( form != NULL ) + close_tform(form, options); + } + + static PyObject *create() + { + return PyCObject_FromVoidPtr(new plgform_t(), destroy); + } + + static void destroy(void *obj) + { + delete (plgform_t *)obj; + } +}; +// + +// +//--------------------------------------------------------------------------- +#define DECL_PLGFORM plgform_t *plgform = (plgform_t *) PyCObject_AsVoidPtr(py_link); +static PyObject *plgform_new() +{ + return plgform_t::create(); +} + +static bool plgform_show( + PyObject *py_link, + PyObject *py_obj, + const char *caption, + int options = FORM_MDI|FORM_TAB|FORM_MENU|FORM_RESTORE) +{ + DECL_PLGFORM; + return plgform->show(py_obj, caption, options); +} + +static void plgform_close( + PyObject *py_link, + int options) +{ + DECL_PLGFORM; + plgform->close(options); +} +#undef DECL_PLGFORM +// + +#endif // __PY_PLGFORM__ \ No newline at end of file diff --git a/pywraps/py_plgform.py b/pywraps/py_plgform.py new file mode 100644 index 0000000..eabddd2 --- /dev/null +++ b/pywraps/py_plgform.py @@ -0,0 +1,108 @@ +import _idaapi + +# +class PluginForm(object): + """ + PluginForm class. + + This form can be used to host additional controls. Please check the PyQt example. + """ + + FORM_MDI = 0x01 + """start by default as MDI""" + FORM_TAB = 0x02 + """attached by default to a tab""" + FORM_RESTORE = 0x04 + """restore state from desktop config""" + FORM_ONTOP = 0x08 + """form should be "ontop""" + FORM_MENU = 0x10 + """form must be listed in the windows menu (automatically set for all plugins)""" + FORM_CENTERED = 0x20 + """form will be centered on the screen""" + FORM_PERSIST = 0x40 + """form will persist until explicitly closed with Close()""" + + + def __init__(self): + """ + """ + self.__clink__ = _idaapi.plgform_new() + + + + def Show(self, caption, options = 0): + """ + Creates the form if not was not created or brings to front if it was already created + + @param caption: The form caption + @param options: One of PluginForm.FORM_ constants + """ + options |= PluginForm.FORM_MDI|PluginForm.FORM_TAB|PluginForm.FORM_MENU|PluginForm.FORM_RESTORE + return _idaapi.plgform_show(self.__clink__, self, caption, options) + + + @staticmethod + def FormToPyQtWidget(form, ctx = sys.modules['__main__']): + """ + Use this method to convert a TForm* to a QWidget to be used by PyQt + + @param ctx: Context. Reference to a module that already imported SIP and QtGui modules + """ + return ctx.sip.wrapinstance(ctx.sip.voidptr(form).__int__(), ctx.QtGui.QWidget) + + + @staticmethod + def FormToPySideWidget(form, ctx = sys.modules['__main__']): + """ + Use this method to convert a TForm* to a QWidget to be used by PySide + + @param ctx: Context. Reference to a module that already imported QtGui module + """ + return ctx.QtGui.QWidget.FromCObject(form) + + + def OnCreate(self, form): + """ + This event is called when the plugin form is created. + The programmer should populate the form when this event is triggered. + + @return: None + """ + pass + + + def OnClose(self, form): + """ + Called when the plugin form is closed + + @return: None + """ + pass + + + def Close(self, options): + """ + Closes the form. + + @param options: Close options (FORM_SAVE, FORM_NO_CONTEXT, ...) + + @return: None + """ + return _idaapi.plgform_close(self.__clink__) + + FORM_SAVE = 0x1 + """Save state in desktop config""" + + FORM_NO_CONTEXT = 0x2 + """Don't change the current context (useful for toolbars)""" + + FORM_DONT_SAVE_SIZE = 0x4 + """Don't save size of the window""" + + FORM_CLOSE_LATER = 0x8 + """This flag should be used when Close() is called from an event handler""" +# + +plg = PluginForm() +plg.Show("This is it") \ No newline at end of file diff --git a/pywraps/py_qfile.hpp b/pywraps/py_qfile.hpp new file mode 100644 index 0000000..fe40d34 --- /dev/null +++ b/pywraps/py_qfile.hpp @@ -0,0 +1,327 @@ +#ifndef __PY_IDA_QFILE__ +#define __PY_IDA_QFILE__ + +// +/* +# +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: + FILE *fp; + bool own; + qstring fn; + + //-------------------------------------------------------------------------- + void assign(const qfile_t &rhs) + { + fn = rhs.fn; + fp = rhs.fp; + own = false; + } + //-------------------------------------------------------------------------- + bool _from_fp(FILE *fp) + { + if ( fp == NULL ) + return false; + own = false; + fn.sprnt("", fp); + this->fp = fp; + return true; + } + inline void _from_cobject(PyObject *pycobject) + { + _from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)); + } +public: + int __idc_cvt_id__; + //-------------------------------------------------------------------------- + qfile_t(const qfile_t &rhs) + { + assign(rhs); + } + + //-------------------------------------------------------------------------- + qfile_t(PyObject *pycobject = NULL) + { + fp = NULL; + own = true; + fn.qclear(); + __idc_cvt_id__ = PY_ICID_OPAQUE; + if ( pycobject != NULL && PyCObject_Check(pycobject) ) + _from_cobject(pycobject); + } + + //-------------------------------------------------------------------------- + bool opened() + { + return fp != NULL; + } + + //-------------------------------------------------------------------------- + void close() + { + if ( fp == NULL ) + return; + if ( own ) + qfclose(fp); + fp = NULL; + own = true; + } + + //-------------------------------------------------------------------------- + ~qfile_t() + { + close(); + } + + //-------------------------------------------------------------------------- + bool open(const char *filename, const char *mode) + { + close(); + fp = qfopen(filename, mode); + if ( fp == NULL ) + return false; + // Save file name + fn = filename; + own = true; + return true; + } + + //-------------------------------------------------------------------------- + static qfile_t *from_fp(FILE *fp) + { + if ( fp == NULL ) + return NULL; + qfile_t *qf = new qfile_t(); + qf->own = false; + qf->fn.sprnt("", fp); + qf->fp = fp; + return qf; + } + + //-------------------------------------------------------------------------- + // This method can be used to pass a FILE* from C code + static qfile_t *from_cobject(PyObject *pycobject) + { + return PyCObject_Check(pycobject) ? from_fp((FILE *)PyCObject_AsVoidPtr(pycobject)) : NULL; + } + + //-------------------------------------------------------------------------- + static qfile_t *tmpfile() + { + return from_fp(qtmpfile()); + } + + //-------------------------------------------------------------------------- + FILE *get_fp() + { + return fp; + } + + //-------------------------------------------------------------------------- + int seek(int32 offset, int whence = SEEK_SET) + { + return qfseek(fp, offset, whence); + } + + //-------------------------------------------------------------------------- + int32 tell() + { + return qftell(fp); + } + + //-------------------------------------------------------------------------- + PyObject *readbytes(int size, bool big_endian) + { + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + int r = freadbytes(fp, buf, size, big_endian); + if ( r != 0 ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *read(int size) + { + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + int r = qfread(fp, buf, size); + if ( r <= 0 ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromStringAndSize(buf, r); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + PyObject *gets(int size) + { + do + { + char *buf = (char *) malloc(size + 5); + if ( buf == NULL ) + break; + if ( qfgets(buf, size, fp) == NULL ) + { + free(buf); + break; + } + PyObject *ret = PyString_FromString(buf); + free(buf); + return ret; + } while ( false ); + Py_RETURN_NONE; + } + + //-------------------------------------------------------------------------- + int writebytes(PyObject *py_buf, bool big_endian) + { + int sz = PyString_GET_SIZE(py_buf); + void *buf = (void *)PyString_AS_STRING(py_buf); + return fwritebytes(fp, buf, sz, big_endian); + } + + //-------------------------------------------------------------------------- + int write(PyObject *py_buf) + { + if ( !PyString_Check(py_buf) ) + return 0; + return qfwrite(fp, (void *)PyString_AS_STRING(py_buf), PyString_GET_SIZE(py_buf)); + } + + //-------------------------------------------------------------------------- + int puts(const char *str) + { + return qfputs(str, fp); + } + + //-------------------------------------------------------------------------- + int32 size() + { + int pos = qfseek(fp, 0, SEEK_END); + int32 r = qftell(fp); + qfseek(fp, pos, SEEK_SET); + return r; + } + + //-------------------------------------------------------------------------- + int flush() + { + return qflush(fp); + } + + //-------------------------------------------------------------------------- + PyObject *filename() + { + return PyString_FromString(fn.c_str()); + } + + //-------------------------------------------------------------------------- + PyObject *get_char() + { + int ch = qfgetc(fp); + if ( ch == EOF ) + Py_RETURN_NONE; + return Py_BuildValue("c", ch); + } + + //-------------------------------------------------------------------------- + int put_char(char chr) + { + return qfputc(chr, fp); + } +}; +// + +#endif diff --git a/pywraps/py_typeinf.hpp b/pywraps/py_typeinf.hpp new file mode 100644 index 0000000..32d1858 --- /dev/null +++ b/pywraps/py_typeinf.hpp @@ -0,0 +1,308 @@ +#ifndef __PY_TYPEINF__ +#define __PY_TYPEINF__ + +// +//------------------------------------------------------------------------- +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 ) + Py_RETURN_NONE; + + 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) ) + { + PyErr_SetString(PyExc_ValueError, "String expected!"); + return NULL; + } + + size_t sz = get_type_size0(ti, (type_t *)PyString_AsString(tp)); + if ( sz == BADSIZE ) + Py_RETURN_NONE; + + return PyInt_FromLong(sz); +} + +//------------------------------------------------------------------------- +/* +# +def print_type(ea, on_line): + """ + Returns the type of an item + @return: + - None on failure + - The type string with a semicolon. Can be used directly with idc.SetType() + """ + pass +# +*/ +static PyObject *py_print_type(ea_t ea, bool one_line) +{ + char buf[MAXSTR]; + if ( print_type(ea, buf, sizeof(buf), one_line) ) + { + qstrncat(buf, ";", sizeof(buf)); + return PyString_FromString(buf); + } + else + Py_RETURN_NONE; +} + +//------------------------------------------------------------------------- +/* +# +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 = 0) +{ + if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // Unpack + type_t *type = (type_t *) PyString_AsString(py_type); + p_list *fields = (p_list *) PyString_AsString(py_fields); + idc_value_t idc_obj; + error_t err = unpack_object_from_idb( + &idc_obj, + ti, + type, + fields, + ea, + NULL, + pio_flags); + + // Unpacking failed? + if ( err != eOk ) + return Py_BuildValue("(ii)", 0, err); + + // Convert + PyObject *py_ret(NULL); + err = idcvar_to_pyvar(idc_obj, &py_ret); + + // Conversion failed? + if ( err != CIP_OK ) + return Py_BuildValue("(ii)", 0, err); + + PyObject *py_result = Py_BuildValue("(iO)", 1, py_ret); + Py_DECREF(py_ret); + return py_result; +} + +//------------------------------------------------------------------------- +/* +# +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 = 0) +{ + if ( !PyString_Check(py_type) && !PyString_Check(py_fields) && !PyString_Check(py_bytes) ) + { + PyErr_SetString(PyExc_ValueError, "Incorrect argument type!"); + return NULL; + } + + // Get type strings + type_t *type = (type_t *) PyString_AsString(py_type); + p_list *fields = (p_list *) PyString_AsString(py_fields); + + // Make a byte vector + bytevec_t bytes; + bytes.resize(PyString_Size(py_bytes)); + memcpy(bytes.begin(), PyString_AsString(py_bytes), bytes.size()); + + idc_value_t idc_obj; + error_t err = unpack_object_from_bv( + &idc_obj, + ti, + type, + fields, + bytes, + pio_flags); + + // Unpacking failed? + if ( err != eOk ) + return Py_BuildValue("(ii)", 0, err); + + // Convert + PyObject *py_ret(NULL); + err = idcvar_to_pyvar(idc_obj, &py_ret); + + // Conversion failed? + if ( err != CIP_OK ) + return Py_BuildValue("(ii)", 0, err); + + PyObject *py_result = Py_BuildValue("(iO)", 1, py_ret); + Py_DECREF(py_ret); + return py_result; +} + +//------------------------------------------------------------------------- +/* +# +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 = 0) +{ + if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // Convert Python object to IDC object + idc_value_t idc_obj; + if ( !convert_pyobj_to_idc_exc(py_obj, &idc_obj) ) + return NULL; + + // Get type strings + type_t *type = (type_t *) PyString_AsString(py_type); + p_list *fields = (p_list *) PyString_AsString(py_fields); + + // Pack + error_t err = pack_object_to_idb(&idc_obj, ti, type, fields, ea, pio_flags); + return PyInt_FromLong(err); +} + +//------------------------------------------------------------------------- +/* +# +def pack_object_to_bv(obj, ti, tp, fields, base_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 base_ea: base ea used to relocate the pointers in the packed object + @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, + til_t *ti, + PyObject *py_type, + PyObject *py_fields, + ea_t base_ea, + int pio_flags=0) +{ + if ( !PyString_Check(py_type) && !PyString_Check(py_fields) ) + { + PyErr_SetString(PyExc_ValueError, "Typestring must be passed!"); + return NULL; + } + + // Convert Python object to IDC object + idc_value_t idc_obj; + if ( !convert_pyobj_to_idc_exc(py_obj, &idc_obj) ) + return NULL; + + // Get type strings + type_t *type = (type_t *) PyString_AsString(py_type); + p_list *fields = (p_list *) PyString_AsString(py_fields); + + // Pack + relobj_t bytes; + error_t err = pack_object_to_bv( + &idc_obj, + ti, + type, + fields, + &bytes, + NULL, + pio_flags); + do + { + if ( err != eOk ) + break; + if ( !bytes.relocate(base_ea, inf.mf) ) + { + err = -1; + break; + } + return Py_BuildValue("(is#)", 1, bytes.begin(), bytes.size()); + } while ( false ); + return Py_BuildValue("(ii)", 0, err); +} +// + +#endif diff --git a/pywraps/py_ua.hpp b/pywraps/py_ua.hpp new file mode 100644 index 0000000..aa99cb1 --- /dev/null +++ b/pywraps/py_ua.hpp @@ -0,0 +1,793 @@ +#ifndef __PY_UA__ +#define __PY_UA__ + +//------------------------------------------------------------------------- +// +//------------------------------------------------------------------------- +insn_t *insn_t_get_clink(PyObject *self) +{ + return (insn_t *)pyobj_get_clink(self); +} + +//------------------------------------------------------------------------- +op_t *op_t_get_clink(PyObject *self) +{ + return (op_t *)pyobj_get_clink(self); +} + +// + +//------------------------------------------------------------------------- +// + +//------------------------------------------------------------------------- +/* +# +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) +{ + // 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; +} + +//------------------------------------------------------------------------- +/* +# +def term_output_buffer(): + """Use this function to terminate an output buffer.""" + pass +# +*/ +void py_term_output_buffer() +{ + 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 || !PyW_GetNumber(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 || !PyW_GetNumber(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 || !PyW_GetNumber(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 ( PyW_GetNumber(py_off, &v) ) + off = adiff_t(v); + else + off = BADADDR; + + return op == NULL ? false : out_name_expr(*op, ea, off); +} + +//------------------------------------------------------------------------- +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() +{ + return PyCObject_FromVoidPtr(new insn_t(), NULL); +} + +//------------------------------------------------------------------------- +static PyObject *op_t_create() +{ + return PyCObject_FromVoidPtr(new op_t(), NULL); +} + +//------------------------------------------------------------------------- +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 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; + + delete (insn_t *)PyCObject_AsVoidPtr(py_obj); + return true; +} + +//------------------------------------------------------------------------- +// Returns a C link to the global 'cmd' variable +static PyObject *py_get_global_cmd_link() +{ + return PyCObject_FromVoidPtr(&::cmd, NULL); +} + +//------------------------------------------------------------------------- +static PyObject *insn_t_is_canon_insn(int itype) +{ + if ( ph.is_canon_insn(itype) ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +//------------------------------------------------------------------------- +static 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); +} + +//------------------------------------------------------------------------- +static 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); + PyW_GetNumber(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); + PyW_GetNumber(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); + PyW_GetNumber(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); + PyW_GetNumber(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); + PyW_GetNumber(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); +} + +// + +#endif diff --git a/pywraps/py_ua.py b/pywraps/py_ua.py new file mode 100644 index 0000000..7adcb5d --- /dev/null +++ b/pywraps/py_ua.py @@ -0,0 +1,519 @@ +# + +# ----------------------------------------------------------------------- +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 __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 == o_reg and self.reg == 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 __iter__(self): + return (self.Operands[idx] for idx in xrange(0, UA_MAXOP)) + + + def __getitem__(self, idx): + """ + Operands can be accessed directly as indexes + @return op_t: Returns an operand of type op_t + """ + if idx >= UA_MAXOP: + raise KeyError + else: + 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 = 0 # Intel 80x86 +PLFM_Z80 = 1 # 8085, Z80 +PLFM_I860 = 2 # Intel 860 +PLFM_8051 = 3 # 8051 +PLFM_TMS = 4 # Texas Instruments TMS320C5x +PLFM_6502 = 5 # 6502 +PLFM_PDP = 6 # PDP11 +PLFM_68K = 7 # Motoroal 680x0 +PLFM_JAVA = 8 # Java +PLFM_6800 = 9 # Motorola 68xx +PLFM_ST7 = 10 # SGS-Thomson ST7 +PLFM_MC6812 = 11 # Motorola 68HC12 +PLFM_MIPS = 12 # MIPS +PLFM_ARM = 13 # Advanced RISC Machines +PLFM_TMSC6 = 14 # Texas Instruments TMS320C6x +PLFM_PPC = 15 # PowerPC +PLFM_80196 = 16 # Intel 80196 +PLFM_Z8 = 17 # Z8 +PLFM_SH = 18 # Renesas (formerly Hitachi) SuperH +PLFM_NET = 19 # Microsoft Visual Studio.Net +PLFM_AVR = 20 # Atmel 8-bit RISC processor(s) +PLFM_H8 = 21 # Hitachi H8/300, H8/2000 +PLFM_PIC = 22 # Microchip's PIC +PLFM_SPARC = 23 # SPARC +PLFM_ALPHA = 24 # DEC Alpha +PLFM_HPPA = 25 # Hewlett-Packard PA-RISC +PLFM_H8500 = 26 # Hitachi H8/500 +PLFM_TRICORE = 27 # Tasking Tricore +PLFM_DSP56K = 28 # Motorola DSP5600x +PLFM_C166 = 29 # Siemens C166 family +PLFM_ST20 = 30 # SGS-Thomson ST20 +PLFM_IA64 = 31 # Intel Itanium IA64 +PLFM_I960 = 32 # Intel 960 +PLFM_F2MC = 33 # Fujistu F2MC-16 +PLFM_TMS320C54 = 34 # Texas Instruments TMS320C54xx +PLFM_TMS320C55 = 35 # Texas Instruments TMS320C55xx +PLFM_TRIMEDIA = 36 # Trimedia +PLFM_M32R = 37 # Mitsubishi 32bit RISC +PLFM_NEC_78K0 = 38 # NEC 78K0 +PLFM_NEC_78K0S = 39 # NEC 78K0S +PLFM_M740 = 40 # Mitsubishi 8bit +PLFM_M7700 = 41 # Mitsubishi 16bit +PLFM_ST9 = 42 # ST9+ +PLFM_FR = 43 # Fujitsu FR Family +PLFM_MC6816 = 44 # Motorola 68HC16 +PLFM_M7900 = 45 # Mitsubishi 7900 +PLFM_TMS320C3 = 46 # Texas Instruments TMS320C3 +PLFM_KR1878 = 47 # Angstrem KR1878 +PLFM_AD218X = 48 # Analog Devices ADSP 218X +PLFM_OAKDSP = 49 # Atmel OAK DSP +PLFM_TLCS900 = 50 # Toshiba TLCS-900 +PLFM_C39 = 51 # Rockwell C39 +PLFM_CR16 = 52 # NSC CR16 +PLFM_MN102L00 = 53 # Panasonic MN10200 +PLFM_TMS320C1X = 54 # Texas Instruments TMS320C1x +PLFM_NEC_V850X = 55 # NEC V850 and V850ES/E1/E2 +PLFM_SCR_ADPT = 56 # Processor module adapter for processor modules written in scripting languages +PLFM_EBC = 57 # EFI Bytecode +PLFM_MSP430 = 58 # Texas Instruments MSP430 +PLFM_SPU = 59 # Cell Broadband Engine Synergistic Processor Unit + +# +# 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 + + +# ---------------------------------------------------------------------- +class __ph(object): + id = property(lambda self: ph_get_id()) + cnbits = property(lambda self: ph_get_cnbits()) + dnbits = property(lambda self: ph_get_dnbits()) + flag = property(lambda self: ph_get_flag()) + high_fixup_bits = property(lambda self: ph_get_high_fixup_bits()) + icode_return = property(lambda self: ph_get_icode_return()) + instruc = property(lambda self: ph_get_instruc()) + instruc_end = property(lambda self: ph_get_instruc_end()) + instruc_start = property(lambda self: ph_get_instruc_start()) + regCodeSreg = property(lambda self: ph_get_regCodeSreg()) + regDataSreg = property(lambda self: ph_get_regDataSreg()) + regFirstSreg = property(lambda self: ph_get_regFirstSreg()) + regLastSreg = property(lambda self: ph_get_regLastSreg()) + regnames = property(lambda self: ph_get_regnames()) + segreg_size = property(lambda self: ph_get_segreg_size()) + tbyte_size = property(lambda self: ph_get_tbyte_size()) + version = property(lambda self: ph_get_version()) + +ph = __ph() + +# diff --git a/pywraps/pywraps.hpp b/pywraps/pywraps.hpp new file mode 100644 index 0000000..987a84b --- /dev/null +++ b/pywraps/pywraps.hpp @@ -0,0 +1,2 @@ +// Just a proxy header +#include "../pywraps.hpp" \ No newline at end of file diff --git a/pywraps/pywraps.sln b/pywraps/pywraps.sln new file mode 100644 index 0000000..a92f6b4 --- /dev/null +++ b/pywraps/pywraps.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pywraps", "pywraps.vcxproj", "{F43D6BB8-B7D6-486A-82E5-BABBA9848525}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Rel64|Win32 = Rel64|Win32 + Release|Win32 = Release|Win32 + Release64|Win32 = Release64|Win32 + SemiDebug|Win32 = SemiDebug|Win32 + SemiDebugx64|Win32 = SemiDebugx64|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug|Win32.ActiveCfg = Debug|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Debug|Win32.Build.0 = Debug|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Rel64|Win32.ActiveCfg = Rel64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Rel64|Win32.Build.0 = Rel64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release|Win32.ActiveCfg = Release|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release|Win32.Build.0 = Release|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release64|Win32.ActiveCfg = Release64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.Release64|Win32.Build.0 = Release64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebug|Win32.ActiveCfg = SemiDebug|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebug|Win32.Build.0 = SemiDebug|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebugx64|Win32.ActiveCfg = SemiDebugx64|Win32 + {F43D6BB8-B7D6-486A-82E5-BABBA9848525}.SemiDebugx64|Win32.Build.0 = SemiDebugx64|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/pywraps/pywraps.vcproj b/pywraps/pywraps.vcproj new file mode 100644 index 0000000..f742bb8 --- /dev/null +++ b/pywraps/pywraps.vcproj @@ -0,0 +1,1844 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pywraps/pywraps.vcxproj b/pywraps/pywraps.vcxproj new file mode 100644 index 0000000..f14921d --- /dev/null +++ b/pywraps/pywraps.vcxproj @@ -0,0 +1,663 @@ + + + + + Debug + Win32 + + + Rel64 + Win32 + + + Release64 + Win32 + + + Release + Win32 + + + SemiDebugx64 + Win32 + + + SemiDebug + Win32 + + + + {F43D6BB8-B7D6-486A-82E5-BABBA9848525} + pywraps + + + + DynamicLibrary + false + MultiByte + + + DynamicLibrary + false + MultiByte + + + DynamicLibrary + false + MultiByte + + + DynamicLibrary + false + MultiByte + + + DynamicLibrary + false + MultiByte + + + DynamicLibrary + false + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + .\Debug\ + .\Debug\ + true + false + .\Release\ + .\Release\ + false + $(Configuration)\ + $(Configuration)\ + false + $(Configuration)\ + $(Configuration)\ + false + $(Configuration)\ + $(Configuration)\ + false + $(Configuration)\ + $(Configuration)\ + false + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/pywraps.tlb + + + + + Disabled + ..\..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) + _DEBUG;__NT__;__IDP__;MAXSTR=1024;WIN32;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;__PYWRAPS__;_PYWRAPS_DEBUG;NO_OBSOLETE_FUNCS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + .\Debug/pywraps.pch + .\Debug/ + .\Debug/ + .\Debug/ + Level3 + true + EditAndContinue + Cdecl + 4005;4800;4018;%(DisableSpecificWarnings) + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /export:PLUGIN %(AdditionalOptions) + ida.lib;python26_d.lib;%(AdditionalDependencies) + ../../../bin/x86_win_vc/plugins/pywraps.plw + true + ..\..\..\lib\x86_win_vc_32;c:\python26\libs;%(AdditionalLibraryDirectories) + true + .\Debug/pywraps.pdb + false + + + .\Debug/pywraps.lib + MachineX86 + + + true + .\Debug/pywraps.bsc + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/pywraps.tlb + + + + + Disabled + OnlyExplicitInline + ..\..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) + NO_OBSOLETE_FUNCS;NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/pywraps.pch + .\Release/ + .\Release/ + .\Release/ + Level4 + true + ProgramDatabase + Cdecl + + + NDEBUG;%(PreprocessorDefinitions) + 0x0419 + + + /export:PLUGIN %(AdditionalOptions) + ida.lib;python26.lib;%(AdditionalDependencies) + ../../../bin/x86_win_vc/plugins/pywraps.plw + true + ..\..\..\lib\x86_win_vc_32;c:\python26\libs;%(AdditionalLibraryDirectories) + true + .\Release/pywraps.pdb + + + + + .\Release/pywraps.lib + MachineX86 + + + true + .\Release/pywraps.bsc + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/pywraps.tlb + + + + + Disabled + OnlyExplicitInline + ..\..\..\include;c:\Python26\include;%(AdditionalIncludeDirectories) + NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;_CRT_SECURE_NO_WARNINGS;_PYWRAPS_DEBUG;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/pywraps.pch + .\Release/ + .\Release/ + .\Release/ + Level3 + true + ProgramDatabase + Cdecl + 4005;4800;4018;%(DisableSpecificWarnings) + + + NDEBUG;%(PreprocessorDefinitions) + 0x0419 + + + /export:PLUGIN %(AdditionalOptions) + ida.lib;python26.lib;%(AdditionalDependencies) + ../../../bin/x86_win_vc/plugins/pywraps.plw + true + ..\..\..\lib\x86_win_vc_32;c:\python26\libs;%(AdditionalLibraryDirectories) + true + .\Release/pywraps.pdb + + + + + .\Release/pywraps.lib + MachineX86 + + + true + .\Release/pywraps.bsc + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/pywraps.tlb + + + + + Disabled + OnlyExplicitInline + ..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) + NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;_CRT_SECURE_NO_WARNINGS;_PYWRAPS_DEBUG;__EA64__;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/pywraps.pch + .\Release/ + .\Release/ + .\Release/ + Level3 + true + ProgramDatabase + Cdecl + 4005;4800;4018;%(DisableSpecificWarnings) + + + NDEBUG;%(PreprocessorDefinitions) + 0x0419 + + + /export:PLUGIN %(AdditionalOptions) + ida.lib;python26.lib;%(AdditionalDependencies) + ../../../bin/x86_win_vc/plugins/pywraps.p64 + true + ..\..\..\lib\vc.w64;c:\python26\libs;%(AdditionalLibraryDirectories) + true + .\Release/pywraps.pdb + false + + + true + .\Release/pywraps.lib + MachineX86 + + + true + .\Release/pywraps.bsc + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/pywraps.tlb + + + + + Disabled + OnlyExplicitInline + ..\..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) + NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;_CRT_SECURE_NO_WARNINGS;__EA64__;_PYWRAPS_DEBUG;__PYWRAPS__;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/pywraps.pch + .\Release/ + .\Release/ + .\Release/ + Level4 + true + ProgramDatabase + Cdecl + + + NDEBUG;%(PreprocessorDefinitions) + 0x0419 + + + /export:PLUGIN %(AdditionalOptions) + ida.lib;python26.lib;%(AdditionalDependencies) + ../../../bin/x86_win_vc/plugins/pywraps.p64 + true + ..\..\..\lib\vc.w64;c:\python26\libs;%(AdditionalLibraryDirectories) + true + .\Release/pywraps.pdb + + + + + .\Release/pywraps.lib + MachineX86 + + + true + .\Release/pywraps.bsc + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/pywraps.tlb + + + + + Disabled + OnlyExplicitInline + ..\..\..\include;c:\python26\include;%(AdditionalIncludeDirectories) + NDEBUG;WIN32;_WINDOWS;_USRDLL;__NT__;__IDP__;MAXSTR=1024;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/pywraps.pch + .\Release/ + .\Release/ + .\Release/ + Level4 + true + ProgramDatabase + Cdecl + + + NDEBUG;%(PreprocessorDefinitions) + 0x0419 + + + /export:PLUGIN %(AdditionalOptions) + ida.lib;python26.lib;%(AdditionalDependencies) + ../../../bin/x86_win_vc/plugins/pywraps.plw + true + ..\..\..\lib\x86_win_vc_32;c:\python26\libs;%(AdditionalLibraryDirectories) + true + .\Release/pywraps.pdb + + + + + .\Release/pywraps.lib + MachineX86 + + + true + .\Release/pywraps.bsc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + + + true + true + true + true + true + + + true + true + true + true + true + + + + true + true + true + true + true + + + true + true + true + true + true + + + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pywraps/pywraps.vcxproj.filters b/pywraps/pywraps.vcxproj.filters new file mode 100644 index 0000000..5b14810 --- /dev/null +++ b/pywraps/pywraps.vcxproj.filters @@ -0,0 +1,518 @@ + + + + + {9fd646ab-4c34-4457-a88f-0b9efa521a7c} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {c7c2445a-144b-4574-a5a7-4547bf731f79} + + + {a817be2b-11e4-441c-9e1e-ff3825b03d03} + + + {bfbff292-8186-40a9-a90f-09a530e2e5d2} + + + {2ebfadc0-402b-4ef7-b2bf-ab9fb8c5279b} + + + {0ad19987-f808-44a4-865b-985e7f8dca02} + + + {1016cbe2-fa4e-49b5-9e8f-b8fc4a0656d3} + + + {e57b232e-0cdb-4cc6-ad63-80ecfeaa25b9} + + + {cfda7913-ffad-425d-b9ce-295bb647d8e5} + + + {ec48a898-4c2f-443d-9236-c8b58c036050} + + + {12e97676-9f57-4e1f-af42-6bf0cd178ae6} + + + {62308260-bfab-49eb-9b11-7522f7b671d8} + + + {f33949eb-15bb-4926-94e3-1648a4c3bca0} + + + {79a2a07e-7db7-43ac-8c41-fe08777e802c} + + + {714c10e9-2a6f-4b86-a594-b53b28f9fc7e} + + + {22f57328-eb90-4341-bb06-464f6c7b2bd6} + + + {ef594bc3-6670-4e55-a77f-e3bff3193d94} + + + {7092618c-44fe-497b-a229-360bab592f3d} + + + {c5616ba8-8b0d-41a0-bf78-583fdcfb3898} + + + {0cefa3d8-f4b6-4085-b2a2-6b2d97167252} + + + {b21f3b63-396e-4725-83cc-2df9f5b6bb46} + + + {c4a9b8be-70fa-4287-bc47-73db56202dd6} + + + {a103675f-abc2-4f84-a0b8-9ac7403f04db} + + + {ac94c213-e485-48ac-b554-3868e350d671} + + + {833c766e-6e8d-4c00-947f-71a7b34378a6} + + + {8dc47ef4-3da0-4bf8-9ce2-5a9f8896b1b3} + + + {8e65a873-5b8f-4052-a929-e13e9e0f81ac} + + + {b7a7d68d-a3b0-487c-b4d0-bd716dd06280} + + + {959b59d2-acb2-4c89-a828-eece77e502f4} + + + + + py_dbgidd + + + py_dbgidd + + + py_dbgidd + + + py_graph + + + py_graph + + + py_diskio + + + py_diskio + + + py_diskio + + + py_bytes + + + py_bytes + + + deploy + + + deploy + + + deploy + + + deploy + + + deploy + + + deploy + + + deploy + + + deploy + + + deploy + + + deploy + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + deploy\examples + + + py_idaapi + + + py_idaapi + + + py_typeinf + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + swig.i + + + py_gdl + + + py_gdl + + + py_ua + + + py_ua + + + IDAPython + + + IDAPython + + + IDAPython + + + IDAPython + + + py_notifywhen + + + py_lines + + + py_lines + + + py_nalt + + + py_nalt + + + py_loader + + + py_idp + + + py_kernwin + + + py_kernwin + + + py_kernwin\py_custview + + + py_kernwin\py_choose2 + + + py_kernwin\py_cli + + + py_kernwin\py_plgform + + + py_kernwin\py_askusingform + + + py_kernwin\py_askusingform + + + py_kernwin + + + py_kernwin\py_plgform + + + py_kernwin\py_plgform + + + py_idp + + + py_kernwin + + + py_expr + + + + + py_dbgidd + + + py_graph + + + pywraps + + + py_diskio + + + py_bytes + + + py_bytes + + + IDAPython + + + IDAPython + + + py_notifywhen + + + py_nalt + + + py_kernwin + + + py_kernwin\py_custview + + + py_kernwin\py_choose2 + + + py_kernwin\py_cli + + + swig_stub + + + py_expr + + + + + py_dbgidd + + + py_graph + + + pywraps + + + py_diskio + + + py_diskio + + + py_diskio + + + py_bytes + + + py_bytes + + + py_idaapi + + + py_typeinf + + + py_ua + + + py_notifywhen + + + py_lines + + + py_nalt + + + py_loader + + + py_idp + + + py_utils + + + py_kernwin + + + py_kernwin\py_custview + + + py_kernwin\py_choose2 + + + py_kernwin\py_cli + + + py_kernwin\py_plgform + + + py_kernwin\py_askusingform + + + py_kernwin\py_choose + + + swig_stub + + + py_expr + + + + + IDAPython + + + \ No newline at end of file diff --git a/pywraps/readme.txt b/pywraps/readme.txt new file mode 100644 index 0000000..5ff73f8 --- /dev/null +++ b/pywraps/readme.txt @@ -0,0 +1,116 @@ +============================ +deploy.py - usage +============================ + +The deploy script is used to deploy python and c++ code into SWIG interface files appropriately. +The reason it was created was because working with .i files to put a mixture of C++ and Python code is not practical for testing and development process. + +In SWIG, there are three sections: + +Inline +--------- + +C++ code will be wrapped by SWIG. + +In SWIG .i files the inline code is marked with: + %inline %{ + C++ code + %} + +In deploy.py supporting files the code to be pasted into .i files is marked with: + // + C++ code + // + + +Code +------- +C++ code will be pasted and compiled into the wrapped module but will not be wrapped by SWIG. + +In SWIG .i files the code is marked with: + %{ + C++ code + %} + +Similarly, for deploy.py supporting files should be marked with: + // + C++ code + // + +Pythoncode +-------------- + +Python code allows you to insert Python code into the final Python module. + +In SWIG .i files, the extra python code is marked with: + %pythoncode %{ + Py code + %} + +In deploy.py supporting python files, it is marked with: + # + Py code + # + +Using deploy.py +------------------ +Make sure that all of the 3 code markers exist in the interface files and deploy.py support files (C++ or Python). + +As an example, let us interpret the meaning of: + deploy.py py_idaapi py_idaapi.hpp,py_idaapi.py ..\swig\idaapi.i +It means: + NAME = py_idaapi + ...take code snips from py_idaapi.hpp and py_idaapi.py + ...and paste the code there into idaapi.i SWIG interface file + +Now remember that both the input files have the special markers (discussed above) and so does idaapi.i file + + +============================ +linkgen.py - usage +============================ +TODO + + +============================ +swigdocs.py - usage +============================ + +The swigdocs script will extract python comments from SWIG interface files (*.i). + +There are two places where Python code documentation can be found: + 1. In the "%pythoncode %{" section, we extract all the python code because it could contain docstrings. + Inside the pythoncode section, one can find embedded commented that are commented out. + Because they are commented out, the documentation generator will miss them. The swigdocs script will remove the comment character: +# +# def OnClose(self): +# """ +# Called when the window is being closed. +# This callback is mandatory. +# @return: nothing +# """ +# pass +# + After swigdocs finishes, the output will contain all the python code and all the commented code (now uncommented). + + 2. In the "%inline %{" section (in C++ code), one can find functions comments like this: +/* +# +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) +{ + ...... +} + In this case, the code inside tag will be extracted as well. + + +After swigdocs finishes, the output is a Python file containing all code and comments extracted from the *.i file(s). \ No newline at end of file diff --git a/pywraps/sidaapi.py b/pywraps/sidaapi.py new file mode 100644 index 0000000..73c090a --- /dev/null +++ b/pywraps/sidaapi.py @@ -0,0 +1,225 @@ +import sys +import sidc + +BUF = None + +# ---------------------------------------------------------------------- +def ua_next_byte(): + p = cmd.ea + if p >= len(BUF): + return None + ch = ord(BUF[p]) + ua_seek(p+1) + cmd.size += 1 + return ch + +def ua_next_word(): + b1 = ua_next_byte() + b2 = ua_next_byte() + return (b2 << 8) | b1 + +def ua_next_long(): + w1 = ua_next_word() + w2 = ua_next_word() + return (w2 << 16) | w1 + +def ua_next_qword(): + d1 = ua_next_long() + d2 = ua_next_long() + return (d2 << 32) | d1 + +# ---------------------------------------------------------------------- +def ua_set_data(data): + global BUF + BUF = data + ua_seek(0) + +# ---------------------------------------------------------------------- +def ua_seek(v): + cmd.ea = v + +# ---------------------------------------------------------------------- +def ua_get_seek(): + return cmd.ea + +# ---------------------------------------------------------------------- +def init_file(fn): + global BUF, PTR + try: + f = open(fn, "rb") + BUF = f.read() + ua_seek(0) + f.close() + except Exception, e: + print "init_file()", e + return False + return True + +# ---------------------------------------------------------------------- +class cvar_t: + def __init__(self): + self.uFlag = 0 + self.gl_comm = 1 +# ---------------------------------------------------------------------- +cvar = cvar_t() +cmd = sidc.insn_t() + +# ---------------------------------------------------------------------- +class processor_t(object): + def __init__(self): + # This is an opaque object + self.__idc_cvt_id__ = 2 + + # 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 + """ + return idpdef['plnames'][0] + ':' + ':'.join(idpdef['psnames']) + + def get_uFlag(self): + """Use this utility function to retrieve the 'uFlag' global variable""" + return _idaapi.cvar.uFlag + + def get_auxpref(self): + return self.cmd.auxpref + +# ---------------------------------------------------------------------- +BADADDR = 0xFFFFFFFFFFFFFFFFL + +# ---------------------------------------------------------------------- + +""" +# Colors dump + +attrs = [x for x in dir(idaapi) if x.startswith('SCOLOR')] +for x in attrs: + print "%s =%r;" % (x, getattr(idaapi, x)) + +attrs = [x for x in dir(idaapi) if x.startswith('COLOR')] +for x in attrs: + v = getattr(idaapi, x); + if isinstance(v, str): + v = ord(x[0]) + print "%s =%r;" % (x, v) +""" + +SCOLOR_ADDR ='('; +SCOLOR_ALTOP ='\x16'; +SCOLOR_ASMDIR ='\x1b'; +SCOLOR_AUTOCMT ='\x04'; +SCOLOR_BINPREF ='\x14'; +SCOLOR_CHAR ='\n'; +SCOLOR_CNAME ='%'; +SCOLOR_CODNAME ='\x1a'; +SCOLOR_COLLAPSED ="'"; +SCOLOR_CREF ='\x0e'; +SCOLOR_CREFTAIL ='\x10'; +SCOLOR_DATNAME ='\x06'; +SCOLOR_DCHAR ='\x1e'; +SCOLOR_DEFAULT ='\x01'; +SCOLOR_DEMNAME ='\x08'; +SCOLOR_DNAME ='\x07'; +SCOLOR_DNUM ='\x1f'; +SCOLOR_DREF ='\x0f'; +SCOLOR_DREFTAIL ='\x11'; +SCOLOR_DSTR ='\x1d'; +SCOLOR_ERROR ='\x12'; +SCOLOR_ESC ='\x03'; +SCOLOR_EXTRA ='\x15'; +SCOLOR_FG_MAX ='('; +SCOLOR_HIDNAME ='\x17'; +SCOLOR_IMPNAME ='"'; +SCOLOR_INSN ='\x05'; +SCOLOR_INV ='\x04'; +SCOLOR_KEYWORD =' '; +SCOLOR_LIBNAME ='\x18'; +SCOLOR_LOCNAME ='\x19'; +SCOLOR_MACRO ='\x1c'; +SCOLOR_NUMBER ='\x0c'; +SCOLOR_OFF ='\x02'; +SCOLOR_ON ='\x01'; +SCOLOR_OPND1 =')'; +SCOLOR_OPND2 ='*'; +SCOLOR_OPND3 ='+'; +SCOLOR_OPND4 =','; +SCOLOR_OPND5 ='-'; +SCOLOR_OPND6 ='.'; +SCOLOR_PREFIX ='\x13'; +SCOLOR_REG ='!'; +SCOLOR_REGCMT ='\x02'; +SCOLOR_RPTCMT ='\x03'; +SCOLOR_SEGNAME ='#'; +SCOLOR_STRING ='\x0b'; +SCOLOR_SYMBOL ='\t'; +SCOLOR_UNAME ='&'; +SCOLOR_UNKNAME ='$'; +SCOLOR_UTF8 ='2'; +SCOLOR_VOIDOP ='\r'; +COLOR_ADDR =40; +COLOR_ADDR_SIZE =8; +COLOR_ALTOP =22; +COLOR_ASMDIR =27; +COLOR_AUTOCMT =4; +COLOR_BG_MAX =12; +COLOR_BINPREF =20; +COLOR_CHAR =10; +COLOR_CNAME =37; +COLOR_CODE =5; +COLOR_CODNAME =26; +COLOR_COLLAPSED =39; +COLOR_CREF =14; +COLOR_CREFTAIL =16; +COLOR_CURITEM =9; +COLOR_CURLINE =10; +COLOR_DATA =6; +COLOR_DATNAME =6; +COLOR_DCHAR =30; +COLOR_DEFAULT =1; +COLOR_DEMNAME =8; +COLOR_DNAME =7; +COLOR_DNUM =31; +COLOR_DREF =15; +COLOR_DREFTAIL =17; +COLOR_DSTR =29; +COLOR_ERROR =18; +COLOR_ESC =3; +COLOR_EXTERN =8; +COLOR_EXTRA =21; +COLOR_FG_MAX =40; +COLOR_HIDLINE =11; +COLOR_HIDNAME =23; +COLOR_IMPNAME =34; +COLOR_INSN =5; +COLOR_INV =4; +COLOR_KEYWORD =32; +COLOR_LIBFUNC =3; +COLOR_LIBNAME =24; +COLOR_LOCNAME =25; +COLOR_MACRO =28; +COLOR_NUMBER =12; +COLOR_OFF = 2; +COLOR_ON = 1; +COLOR_OPND1 =41; +COLOR_OPND2 =42; +COLOR_OPND3 =43; +COLOR_OPND4 =44; +COLOR_OPND5 =45; +COLOR_OPND6 =46; +COLOR_PREFIX =19; +COLOR_REG =33; +COLOR_REGCMT =2; +COLOR_REGFUNC =4; +COLOR_RPTCMT =3; +COLOR_SEGNAME =35; +COLOR_SELECTED =2; +COLOR_STRING =11; +COLOR_SYMBOL =9; +COLOR_UNAME =38; +COLOR_UNKNAME =36; +COLOR_UNKNOWN =7; +COLOR_UTF8 =50; +COLOR_VOIDOP =13; \ No newline at end of file diff --git a/pywraps/sidc.py b/pywraps/sidc.py new file mode 100644 index 0000000..58848df --- /dev/null +++ b/pywraps/sidc.py @@ -0,0 +1,311 @@ +# ---------------------------------------------------------------------- +# +# Misc constants +# +UA_MAXOP = 6 + +# ---------------------------------------------------------------------- +# 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 + +# ---------------------------------------------------------------------- +# asm_t related constants + +# +# asm_t.flag +# +AS_OFFST = 0x00000001 # offsets are 'offset xxx' ? +AS_COLON = 0x00000002 # create colons after data names ? +AS_UDATA = 0x00000004 # can use '?' in data directives + +AS_2CHRE = 0x00000008 # double char constants are: "xy +AS_NCHRE = 0x00000010 # char constants are: 'x +AS_N2CHR = 0x00000020 # can't have 2 byte char consts + + # ASCII directives: +AS_1TEXT = 0x00000040 # 1 text per line, no bytes +AS_NHIAS = 0x00000080 # no characters with high bit +AS_NCMAS = 0x00000100 # no commas in ascii directives + +AS_HEXFM = 0x00000E00 # format of hex numbers: +ASH_HEXF0 = 0x00000000 # 34h +ASH_HEXF1 = 0x00000200 # h'34 +ASH_HEXF2 = 0x00000400 # 34 +ASH_HEXF3 = 0x00000600 # 0x34 +ASH_HEXF4 = 0x00000800 # $34 +ASH_HEXF5 = 0x00000A00 # <^R > (radix) +AS_DECFM = 0x00003000 # format of dec numbers: +ASD_DECF0 = 0x00000000 # 34 +ASD_DECF1 = 0x00001000 # #34 +ASD_DECF2 = 0x00002000 # 34. +ASD_DECF3 = 0x00003000 # .34 +AS_OCTFM = 0x0001C000 # format of octal numbers: +ASO_OCTF0 = 0x00000000 # 123o +ASO_OCTF1 = 0x00004000 # 0123 +ASO_OCTF2 = 0x00008000 # 123 +ASO_OCTF3 = 0x0000C000 # @123 +ASO_OCTF4 = 0x00010000 # o'123 +ASO_OCTF5 = 0x00014000 # 123q +ASO_OCTF6 = 0x00018000 # ~123 +AS_BINFM = 0x000E0000 # format of binary numbers: +ASB_BINF0 = 0x00000000 # 010101b +ASB_BINF1 = 0x00020000 # ^B010101 +ASB_BINF2 = 0x00040000 # %010101 +ASB_BINF3 = 0x00060000 # 0b1010101 +ASB_BINF4 = 0x00080000 # b'1010101 +ASB_BINF5 = 0x000A0000 # b'1010101' + +AS_UNEQU = 0x00100000 # replace undefined data items + # with EQU (for ANTA's A80) +AS_ONEDUP = 0x00200000 # One array definition per line +AS_NOXRF = 0x00400000 # Disable xrefs during the output file generation +AS_XTRNTYPE = 0x00800000 # Assembler understands type of extrn + # symbols as ":type" suffix +AS_RELSUP = 0x01000000 # Checkarg: 'and','or','xor' operations + # with addresses are possible +AS_LALIGN = 0x02000000 # Labels at "align" keyword + # are supported. +AS_NOCODECLN = 0x04000000 # don't create colons after code names +AS_NOTAB = 0x08000000 # Disable tabulation symbols during the output file generation +AS_NOSPACE = 0x10000000 # No spaces in expressions +AS_ALIGN2 = 0x20000000 # .align directive expects an exponent rather than a power of 2 + # (.align 5 means to align at 32byte boundary) +AS_ASCIIC = 0x40000000 # ascii directive accepts C-like + # escape sequences (\n,\x01 and similar) +AS_ASCIIZ = 0x80000000 # ascii directive inserts implicit + # zero byte at the end + +# ---------------------------------------------------------------------- +# processor_t related constants + +IDP_INTERFACE_VERSION = 76 +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.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 + +# ---------------------------------------------------------------------- +OOF_SIGNMASK = 0x0003 # sign symbol (+/-) output: +OOFS_IFSIGN = 0x0000 # output sign if needed +OOFS_NOSIGN = 0x0001 # don't output sign, forbid the user to change the sign +OOFS_NEEDSIGN = 0x0002 # always out sign (+-) +OOF_SIGNED = 0x0004 # output as signed if < 0 +OOF_NUMBER = 0x0008 # always as a number +OOF_WIDTHMASK = 0x0070 # width of value in bits: +OOFW_IMM = 0x0000 # take from x.dtyp +OOFW_8 = 0x0010 # 8 bit width +OOFW_16 = 0x0020 # 16 bit width +OOFW_24 = 0x0030 # 24 bit width +OOFW_32 = 0x0040 # 32 bit width +OOFW_64 = 0x0050 # 32 bit width +OOF_ADDR = 0x0080 # output x.addr, otherwise x.value +OOF_OUTER = 0x0100 # output outer operand +OOF_ZSTROFF = 0x0200 # meaningful only if isStroff(uFlag) + # append a struct field name if + # the field offset is zero? + # if AFL_ZSTROFF is set, then this flag + # is ignored. +OOF_NOBNOT = 0x0400 # prohibit use of binary not +OOF_SPACES = 0x0800 # do not suppress leading spaces + # currently works only for floating point numbers + + +# ---------------------------------------------------------------------- +class insn_t(object): + def __init__(self, noperands = UA_MAXOP): + self.auxpref = 0 + self.cs = 0 + self.ea = 0 + self.flags = 0 + self.insnpref = 0 + self.ip = 0 + self.itype = 0 + self.n = 0 + self.segpref = 0 + self.size = 0 + self.Operands = [] + + # store the number of operands + self.n = noperands + + # create operands + for i in xrange(0, noperands): + op = op_t() + op.n = i + self.Operands.append(op) + setattr(self, 'Op%d' % (i+1), op) + def __getitem__(self, i): + return self.Operands[i] + +# ---------------------------------------------------------------------- +class op_t(object): + def __init__(self): + self.addr = 0 + self.dtyp = 0 + self.flags = 0 + self.n = 0 + self.offb = 0 + self.offo = 0 + self.reg = 0 + self.specval = 0 + self.specflag1 = 0 + self.specflag2 = 0 + self.specflag3 = 0 + self.specflag4 = 0 + self.type = 0 + self.value = 0 + + # make sure reg and phrase have the same value + def __setattr__(self, name, value): + if name == 'reg' or name == 'phrase': + object.__setattr__(self, 'reg', value) + object.__setattr__(self, 'phrase', value) + else: + object.__setattr__(self, name, value) diff --git a/pywraps/swig_stub.cpp b/pywraps/swig_stub.cpp new file mode 100644 index 0000000..28ee2dc --- /dev/null +++ b/pywraps/swig_stub.cpp @@ -0,0 +1,6 @@ +#include "swig_stub.h" + +PyObject *SWIG_NewPointerObj(void *ptr, void *type, int flags) +{ + return PyCObject_FromVoidPtr(ptr, NULL); +} diff --git a/pywraps/swig_stub.h b/pywraps/swig_stub.h new file mode 100644 index 0000000..307b8bb --- /dev/null +++ b/pywraps/swig_stub.h @@ -0,0 +1,27 @@ +#ifndef __SWIG_STUB__ +#define __SWIG_STUB__ + +#include + +#define SWIG_as_voidptr(a) const_cast< void * >(static_cast< const void * >(a)) + +PyObject *SWIG_NewPointerObj(void *ptr, void *type, int flags); + +namespace Swig +{ + class DirectorException + { + public: + const char *getMessage() const + { + return "NULL"; + } + }; +} + +#define SWIG_RUNTIME_VERSION "4" + +// Some fake SWIG types +#define SWIGTYPE_p_member_t NULL + +#endif \ No newline at end of file