diff --git a/CMakeLists.txt b/CMakeLists.txt index fb4edd0..6c3913f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,83 +5,6 @@ cmake_policy(SET CMP0015 NEW) set(CMAKE_BUILD_TYPE Release) -if (WIN32) - add_definitions(-D__NT__ - -DUNICODE - -DWIN32 - -D__IDP__) - - find_library(IDA32_LIB - NAMES "ida" - PATHS "${IDA_SDK}/lib/x86_win_vc_32") - - find_library(IDA64_LIB - NAMES "ida" - PATHS "${IDA_SDK}/lib/x86_win_vc_64") - - find_library(MINHOOK_LIB - NAMES "libMinHook-x86-v90-md" - PATHS "lib/MinHook_13_lib/lib") - - find_library(PYTHON_LIB - NAMES "python27" - PATHS "${PYTHON_DIR}/libs") - -elseif (APPLE) - add_definitions(-D__MAC__=1) - find_library (IDA_LIB - NAMES "ida" - PATHS "${IDA_DIR}") - #TODO... -elseif (UNIX) - add_definitions(-D__LINUX__=1) - #TODO... -endif ( ) - -set (SOURCES - src/ida_ipython.cpp - src/ipythonEmbed.cpp - src/ipythonEmbed.h - src/persist.h - src/persist.cpp) - -include_directories(${IDA_SDK}/include - ${PYTHON_DIR}/include - lib/MinHook_13_lib/include) - -add_library(ida32_ipython MODULE ${SOURCES}) -add_library(ida64_ipython MODULE ${SOURCES}) - -if (WIN32) - set (IDA32_SUFFIX ".plw") - set (IDA64_SUFFIX ".p64") -elseif (APPLE) -# set (IDA_SUFFIX ".pmc") -elseif (UNIX) -# set (IDA_SUFFIX ".plx") -endif () - -set_target_properties (ida32_ipython - PROPERTIES - SUFFIX ${IDA32_SUFFIX} - OUTPUT_NAME ida_ipython) - -set_target_properties (ida64_ipython - PROPERTIES - SUFFIX ${IDA64_SUFFIX} - COMPILE_DEFINITIONS __EA64__ - OUTPUT_NAME ida_ipython) - -target_link_libraries (ida32_ipython - ${IDA32_LIB} - ${MINHOOK_LIB} - ${PYTHON_LIB}) - -target_link_libraries (ida64_ipython - ${IDA64_LIB} - ${MINHOOK_LIB} - ${PYTHON_LIB}) - #Read the launch script and escape chars file(READ "launch_ida.py" LAUNCH_IDA_PY) string(REPLACE "\"" "\\\"" LAUNCH_IDA_PY "${LAUNCH_IDA_PY}") @@ -128,7 +51,7 @@ string(REPLACE file(WRITE notebook/kernels/ida32/kernel.json ${IDA_32_KERNEL}) file(WRITE notebook/kernels/ida64/kernel.json ${IDA_64_KERNEL}) -install(TARGETS ida32_ipython DESTINATION ${IDA_DIR}/plugins) -install(TARGETS ida64_ipython DESTINATION ${IDA_DIR}/plugins) -install(FILES python/ipythonEmbed.py DESTINATION ${IDA_DIR}/python) -install(FILES idc/nothing.idc DESTINATION ${IDA_DIR}/idc) +#install(TARGETS ida32_ipython DESTINATION ${IDA_DIR}/plugins) +#install(TARGETS ida64_ipython DESTINATION ${IDA_DIR}/plugins) +#install(FILES python/ipythonEmbed.py DESTINATION ${IDA_DIR}/python) +#install(FILES idc/nothing.idc DESTINATION ${IDA_DIR}/idc) diff --git a/README.md b/README.md index 96fc91a..baca37f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # What's New - Improve python plugin load order (prevents crashes when python does not load correctly) - Add in safe IDA process termination (Special thanks to @tmr232 for this) +- Pure Python #What and Why? This is a plugin to embed an IPython kernel in IDA Pro. The Python ecosystem has amazing libraries (and communities) for scientific computing. IPython itself is great for exploratory data analysis. Using tools such as the IPython notebook make it easy to share code and explanations with rich media. IPython makes using IDAPython and interacting with IDA programmatically really fun and easy. @@ -27,25 +28,26 @@ Another useful case is using IPython notebooks. More examples..soon... #How the plugin works -IDA is predominantly single threaded application, so we cannot safely run the kernel in a separate thread. So instead of using another thread a hook is created on the QT process events function and the `do_one_iteration` method of the ipython kernel is executed each frame. +IDA is predominantly single threaded application, so we cannot safely run the kernel in a separate thread. +So instead of using another thread, a timer is registered via IDAPython and the `do_one_iteration` method of the ipython kernel is executed each frame. #Installation I suggest using the [Anaconda](http://continuum.io/downloads) distribution of Python as it comes with all the required python libraries pre-built and installed. To get IDA to use Anaconda, simply set the PYTHONHOME enviroment variable. Alternatively you can install IPython and the dependencies separately. -This plugin should work on all 6.X x86 QT versions of IDA on Windows. +This plugin should work on all 6.X x86 QT versions of IDA on Windows, Linux, and OSX (only tested on Windows). ##Basic Installation and QTConsole 1. Download and extract the [release](https://github.com/james91b/ida_ipython/releases/latest) -2. Copy the contents of the `plugins` and `python` directories under IDA's installation directory. -4. Launch IDA. -5. At the command line (Windows), start an IPython qtconsole with the kernel instance (outputted in the IDA console) e.g `ipython qtconsole --existing kernel-4264.json` +2. Copy `plugin\ida_ipython.py` into IDA's `plugins` directory +4. Launch IDA +5. Under the `View` menu, click `IDAIPython QtConsole` or +6. At the command line, start an IPython qtconsole with the kernel instance (outputted in the IDA console) e.g `jupyter qtconsole --existing kernel-4264.json` ##Using the Notebook -1. Copy `idc` directory to your IDA directory. (the `nothing.idc` script is used to pass command line parameters to the plugin) -2. Change the paths to the `idaq.exe` and `idaq64.exe` executables in the `kernel.json` under the `notebook\kernels\ida32` +1. Change the paths to the `idaq.exe` and `idaq64.exe` executables in the `kernel.json` under the `notebook\kernels\ida32` and `notebook\kernels\ida64` directories respectively -3. Install the kernels using `jupyter-kernelspec install` (e.g. `jupyter-kernelspec install --user notebook\kernels\ida64`) -4. When starting a notebook, choose the `IDA32` or `IDA64` kernels, depending on your desired IDA version. +1. Install the kernels using `jupyter-kernelspec install` (e.g. `jupyter-kernelspec install --user notebook\kernels\ida64`) +1. When starting a notebook, choose the `IDA32` or `IDA64` kernels, depending on your desired IDA version. #How to Build 1. Install cmake diff --git a/idc/nothing.idc b/idc/nothing.idc deleted file mode 100644 index e69de29..0000000 diff --git a/launch_ida.py b/launch_ida.py index 1c632d3..27192c0 100644 --- a/launch_ida.py +++ b/launch_ida.py @@ -2,21 +2,22 @@ import subprocess import os -CONNECTION_ARG = '-Snothing.idc -f {file}' def launch_ida(): print sys.argv print str(os.getpid()) - conn = CONNECTION_ARG.format(file=sys.argv[1]) - ida_location=sys.argv[2] + connection_file = sys.argv[1] + ida_location = sys.argv[2] ida_process = subprocess.Popen( - [ida_location, conn], - env=dict( - PARENT_PROCESS_PID=str(os.getpid()), - **os.environ - ) + [ida_location], + env=dict( + PARENT_PROCESS_PID=str(os.getpid()), + JUPYTER_CONNECTION=connection_file, + **os.environ ) + ) ida_process.wait() + if __name__ == '__main__': launch_ida() diff --git a/plugin/ida_ipython.py b/plugin/ida_ipython.py new file mode 100644 index 0000000..a552509 --- /dev/null +++ b/plugin/ida_ipython.py @@ -0,0 +1,229 @@ +import traceback +import os +import sys +import platform +import subprocess +import idaapi +import contextlib + +idaapi.refresh_idaview() +import idc + +idc.Refresh() + +# This is a hack to get zmq to work with the Anaconda distribution and IDA. +try: + platform.python_implementation() +except ValueError: + sys.version = '2.7.5 |Anaconda 2.1.0 (32-bit)| (default, May 31 2013, 10:43:53) [MSC v.1500 32 bit (Intel)]' + +import __main__ +from ipykernel.kernelapp import IPKernelApp +from IPython.utils.frame import extract_module_locals + + +def add_idaipython_menu(callback): + class MyHandler(idaapi.action_handler_t): + def __init__(self): + idaapi.action_handler_t.__init__(self) + + def activate(self, ctx): + callback() + return 1 + + def update(self, ctx): + return idaapi.AST_ENABLE_ALWAYS + + action_name = 'IDAIPython:QtConsole' + action_desc = idaapi.action_desc_t( + action_name, + 'IDAIPython QtConsole', + MyHandler(), + '', + 'Launch IDAIPython QtConsole', + -1) + + idaapi.register_action(action_desc) + + idaapi.attach_action_to_menu( + 'View/', + action_name, + idaapi.SETMENU_INS) + + +def remove_idaipython_menu(): + idaapi.detach_action_from_menu('View/IDAIPython QtConsole', 'IDAIPython:QtConsole') + + +class IDAIPython(idaapi.plugin_t): + wanted_name = "IDA IPython" + wanted_hotkey = "" + flags = idaapi.PLUGIN_FIX + comment = "" + help = "" + + def init(self): + + self.kernel_app = None + self.menu_items = [] + self.qtconsole_processes = [] + + argv = None + connection_file = os.environ.get("JUPYTER_CONNECTION", None) + if connection_file: + argv = ['-f', connection_file] + + kernel_iteration = self.start(argv) + + def timer_callback(): + kernel_iteration() + return int(1000 * self.kernel_app.kernel._poll_interval) + + self.timer = idaapi.register_timer(int(1000 * self.kernel_app.kernel._poll_interval), timer_callback) + + return idaapi.PLUGIN_KEEP + + def run(self, args): + pass + + def term(self): + idaapi.unregister_timer(self.timer) + self.kill_qtconsoles() + self.remove_menus() + + def embed_kernel(self, module=None, local_ns=None, **kwargs): + """Embed and start an IPython kernel in a given scope. + + Parameters + ---------- + module : ModuleType, optional + The module to load into IPython globals (default: caller) + local_ns : dict, optional + The namespace to load into IPython user namespace (default: caller) + + kwargs : various, optional + Further keyword args are relayed to the IPKernelApp constructor, + allowing configuration of the Kernel. Will only have an effect + on the first embed_kernel call for a given process. + + """ + # get the app if it exists, or set it up if it doesn't + if IPKernelApp.initialized(): + app = IPKernelApp.instance() + else: + app = IPKernelApp.instance(**kwargs) + app.initialize(sys.argv) + # Undo unnecessary sys module mangling from init_sys_modules. + # This would not be necessary if we could prevent it + # in the first place by using a different InteractiveShell + # subclass, as in the regular embed case. + main = app.kernel.shell._orig_sys_modules_main_mod + if main is not None: + sys.modules[app.kernel.shell._orig_sys_modules_main_name] = main + + # load the calling scope if not given + (caller_module, caller_locals) = extract_module_locals(1) + if module is None: + module = caller_module + if local_ns is None: + local_ns = caller_locals + + app.kernel.user_module = None + app.kernel.user_ns = None + app.shell.set_completer_frame() + + if app.poller is not None: + app.poller.start() + + app.kernel.start() + return app + + @contextlib.contextmanager + def capture_output_streams(self): + self._capture_output_streams() + try: + yield + finally: + self._release_output_streams() + + def _capture_output_streams(self): + sys.__stdout__, sys.__stderr__, sys.stdout, sys.stderr = sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__ + + def _release_output_streams(self): + sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__ = sys.__stdout__, sys.__stderr__, sys.stdout, sys.stderr + + def find_python_dir(self): + # We need to get the python directory like this, because + # sys.executable will return idaq.exe. This just goes two + # directories up from os.py location + return os.path.dirname(os.path.dirname(os.__file__)) + + def start_qtconsole(self): + try: + if self.kernel_app: + python_directory = self.find_python_dir() + cmd_line = [ + "{}/pythonw".format(python_directory), + "-m", "qtconsole", + "--existing", self.kernel_app.connection_file + ] + process = subprocess.Popen(cmd_line, + stdin=None, + stdout=None, + stderr=None, + close_fds=True) + self.qtconsole_processes.append(process) + else: + print "Error: No kernel defined!" + except Exception, e: + traceback.print_exc() + + def kill_qtconsoles(self): + for process in self.qtconsole_processes: + process.kill() + + def remove_menus(self): + try: + for menu_item in self.menu_items: + idaapi.del_menu_item(menu_item) + except: + remove_idaipython_menu() + + def add_idaipython_menu(self): + try: + menu_item = idaapi.add_menu_item('View/', 'IDAIPython QtConsole', '', 0, self.start_qtconsole, tuple()) + self.menu_items.append(menu_item) + except: + add_idaipython_menu(self.start_qtconsole) + + def start(self, argv=None): + try: + with self.capture_output_streams(): + if argv: + sys.argv = argv + + self.kernel_app = self.embed_kernel(module=__main__, local_ns={}) + """ + Starting with ipython 4.2.0 whenever certain exceptions are thrown, there is a call to get_terminal_size(). + in that function , in case environment variables for "COLUMNS" and "LINES" are not defined there is a call + to sys.__stdout__.fileno() in order to get a handle to the current terminal. IDAPythonStdOut doesn't have an attribute fileno + so the call fails , and the kernel dies. the right way to solve it, is add AttributeError to the try/except in get_terminal_size. + a work around is to add this 2 environment variables + """ + os.environ["COLUMNS"] = "80" + os.environ["LINES"] = "24" + + def kernel_iteration(): + with self.capture_output_streams(): + self.kernel_app.kernel.do_one_iteration() + + self.add_idaipython_menu() + + return kernel_iteration + except Exception, e: + traceback.print_exc() + raise + + +def PLUGIN_ENTRY(): + return IDAIPython() diff --git a/python/ipythonEmbed.py b/python/ipythonEmbed.py deleted file mode 100644 index 7bd423b..0000000 --- a/python/ipythonEmbed.py +++ /dev/null @@ -1,161 +0,0 @@ -import traceback - -try: - import os - import sys - import platform - import subprocess - import idaapi - import atexit - import contextlib - - # This is a hack to get zmq to work with the Anaconda distribution and IDA. - try: - platform.python_implementation() - except ValueError: - sys.version = '2.7.5 |Anaconda 2.1.0 (32-bit)| (default, May 31 2013, 10:43:53) [MSC v.1500 32 bit (Intel)]' - - import __main__ - from ipykernel.kernelapp import IPKernelApp - from IPython.utils.frame import extract_module_locals - - kernel_app = None - menu_items = [] - qtconsole_processes = [] - - - def embed_kernel(module=None, local_ns=None, **kwargs): - """Embed and start an IPython kernel in a given scope. - - Parameters - ---------- - module : ModuleType, optional - The module to load into IPython globals (default: caller) - local_ns : dict, optional - The namespace to load into IPython user namespace (default: caller) - - kwargs : various, optional - Further keyword args are relayed to the IPKernelApp constructor, - allowing configuration of the Kernel. Will only have an effect - on the first embed_kernel call for a given process. - - """ - # get the app if it exists, or set it up if it doesn't - if IPKernelApp.initialized(): - app = IPKernelApp.instance() - else: - app = IPKernelApp.instance(**kwargs) - app.initialize(sys.argv) - # Undo unnecessary sys module mangling from init_sys_modules. - # This would not be necessary if we could prevent it - # in the first place by using a different InteractiveShell - # subclass, as in the regular embed case. - main = app.kernel.shell._orig_sys_modules_main_mod - if main is not None: - sys.modules[app.kernel.shell._orig_sys_modules_main_name] = main - - # load the calling scope if not given - (caller_module, caller_locals) = extract_module_locals(1) - if module is None: - module = caller_module - if local_ns is None: - local_ns = caller_locals - - app.kernel.user_module = None - app.kernel.user_ns = None - app.shell.set_completer_frame() - - if app.poller is not None: - app.poller.start() - - app.kernel.start() - return app - - - @contextlib.contextmanager - def capture_output_streams(): - _capture_output_streams() - try: - yield - finally: - _release_output_streams() - - - def _capture_output_streams(): - sys.__stdout__, sys.__stderr__, sys.stdout, sys.stderr = sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__ - - - def _release_output_streams(): - sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__ = sys.__stdout__, sys.__stderr__, sys.stdout, sys.stderr - - - def find_python_dir(): - # We need to get the python directory like this, because - # sys.executable will return idaq.exe. This just goes two - # directories up from os.py location - return os.path.dirname(os.path.dirname(os.__file__)) - - - def start_qtconsole(): - try: - if kernel_app: - python_directory = find_python_dir() - cmd_line = [ - "{}/pythonw".format(python_directory), - "-m", "qtconsole", - "--existing", kernel_app.connection_file - ] - process = subprocess.Popen(cmd_line, - stdin=None, - stdout=None, - stderr=None, - close_fds=True) - qtconsole_processes.append(process) - else: - print "Error: No kernel defined!" - except Exception, e: - traceback.print_exc() - - - @atexit.register - def term(): - kill_qtconsoles() - remove_menus() - - def kill_qtconsoles(): - for process in qtconsole_processes: - process.kill() - - - def remove_menus(): - for menu_item in menu_items: - idaapi.del_menu_item(menu_item) - - - def add_idaipython_menu(): - menu_item = idaapi.add_menu_item('View/', 'IDAIPython QtConsole', '', 0, start_qtconsole, tuple()) - menu_items.append(menu_item) - - - def start(argv=None): - try: - with capture_output_streams(): - global kernel_app - if argv: - sys.argv = argv - - kernel_app = embed_kernel(module=__main__, local_ns={}) - - def kernel_iteration(): - with capture_output_streams(): - kernel_app.kernel.do_one_iteration() - - add_idaipython_menu() - - return kernel_iteration - except Exception, e: - traceback.print_exc() - raise - -except Exception, e: - traceback.print_exc() diff --git a/releasezip.py b/releasezip.py index 536175e..f5423f0 100644 --- a/releasezip.py +++ b/releasezip.py @@ -11,10 +11,7 @@ def zipdir(path, ziph): def main(version) : release = zipfile.ZipFile('release-{}.zip'.format(version), 'w') zipdir('python', release) - zipdir('idc', release) zipdir('notebook', release) - release.write('build/release/ida_ipython.p64', 'plugins/ida_ipython.p64') - release.write('build/release/ida_ipython.plw', 'plugins/ida_ipython.plw') release.write('README.md') release.close() diff --git a/src/ida_ipython.cpp b/src/ida_ipython.cpp deleted file mode 100644 index 7b8a024..0000000 --- a/src/ida_ipython.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "ipythonEmbed.h" - -#include "pro.h" -#include "ida.hpp" - -#include "persist.h" - - -//Return the arguments in passed via IDC script arguments as a -//python list -PyObject* idc_script_args() -{ - PyObject *py_args = PyList_New(0); - int nArgs = 0; - LPWSTR *szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs); - - if (szArglist == NULL) { - wprintf(L"CommandLineToArgvW failed\n"); - } else { - for (int i = 0; i < nArgs; i++) { - LPWSTR warg = szArglist[i]; - if (warg[0] != '-' || warg[1] != 'S') - continue; - - char carg[512]; - wcstombs(carg, warg + 2, 512); - - qstrvec_t out_args; - parse_command_line(carg, &out_args); - - for (unsigned int j = 0; j < out_args.size(); j++) { - PyList_Insert(py_args, j, PyString_FromString(out_args[j].c_str())); - } - } - } - - LocalFree(szArglist); - return py_args; -} - -int idaapi init(void) -{ - PyObject* idc_args = idc_script_args(); - int success = -1; - - IPYTHONEMBED_STATUS status = ipython_embed_start(idc_args); - if (status != IPYTHONEMBED_OK) { - switch (status) { - case IPYTHONEMBED_MINHOOK_INIT_FAILED: - warning("[IDA IPython] Failed to initialize MinHook"); - break; - case IPYTHONEMBED_CREATE_HOOK_FAILED: - warning("[IDA IPython] Failed to create the QT hook"); - break; - case IPYTHONEMBED_ENABLE_HOOK_FAILED: - warning("[IDA IPython] Failed to enable the QT hook"); - break; - default: - warning("[IDA IPython] Failed to start ipython kernel"); - } - return PLUGIN_SKIP; - } - - - /* Try and make the module persist in memory until termination. Failing to do so can cause IDA to crash - when it terminates the plugin. */ - success = persist(); - if (0 != success) { - warning("[IDA IPython] Failed to lock the module in memory"); - return PLUGIN_SKIP; - } - - return PLUGIN_KEEP; -} - -void idaapi term(void) -{ - ipython_embed_term(); -} - -void idaapi run(int options) -{ - ipython_start_qtconsole(); -} -//-------------------------------------------------------------------------- -// -// PLUGIN DESCRIPTION BLOCK -// -//-------------------------------------------------------------------------- -static char wanted_name[] = "IDA IPython QTConsole"; -static char comment[] = "Runs an IPython Kernel within IDA"; -static char help[] = "This plugin allows the user to run an IPython kernel within IDA\n"; - -plugin_t PLUGIN = -{ - IDP_INTERFACE_VERSION, - PLUGIN_FIX, // plugin flags - init, // initialize - term, // terminate. this pointer may be NULL. - run, // invoke plugin - comment, // long comment about the plugin - // it could appear in the status line - // or as a hint - help, // multiline help about the plugin - wanted_name, // the preferred short name of the plugin - NULL // the preferred hotkey to run the plugin -}; diff --git a/src/ipythonEmbed.cpp b/src/ipythonEmbed.cpp deleted file mode 100644 index 33a4486..0000000 --- a/src/ipythonEmbed.cpp +++ /dev/null @@ -1,256 +0,0 @@ -#include "ipythonEmbed.h" -#include -#include -#include - -static const char IPYTHON_EMBED_MODULE[] = "ipythonEmbed"; -static const char IPYTHON_EMBED_START_METHOD_NAME[] = "start"; -static const char IPYTHON_EMBED_START_QTCONSOLE_METHOD_NAME[] = "start_qtconsole"; -static const char QT4_MODULE_NAME[] = "QtCore4.dll"; -static const char QT5_MODULE_NAME[] = "Qt5Core.dll"; -static const char EVENT_LOOP_FUNC_NAME[] = "?processEvents@QEventDispatcherWin32@QT@@UAE_NV?$QFlags@W4ProcessEventsFlag@QEventLoop@QT@@@2@@Z"; -static const char PARENT_PID_ENV_NAME[] = "PARENT_PROCESS_PID"; -static const char IDA_PYTHON_PLUGIN[] = "python"; - -static PyObject* kernel_do_one_iteration = NULL; -static PyObject* commandline_args = NULL; -static bool attempted_start_kernel = false; -static bool python_loaded = false; - -typedef int (__fastcall *tQEventDispatcherWin32)(void*, void*, int); -tQEventDispatcherWin32 pQEventDispatcherWin32 = NULL; - -PyObject* start_ipython_kernel(PyObject* cmdline) -{ - PyObject *ipython_embed_module = NULL, - *ipython_start_func = NULL, - *ipython_kernel = NULL, - *arglist = NULL; - - ipython_embed_module = PyImport_ImportModule(IPYTHON_EMBED_MODULE); - if (ipython_embed_module == NULL) { - goto error; - } - - ipython_start_func = PyObject_GetAttrString(ipython_embed_module, IPYTHON_EMBED_START_METHOD_NAME); - if (ipython_start_func == NULL) { - goto error; - } - - if (PyCallable_Check(ipython_start_func)) { - if (cmdline != NULL) { - arglist = Py_BuildValue("(O)", cmdline); - ipython_kernel = PyObject_CallObject(ipython_start_func, arglist); - } else { - ipython_kernel = PyObject_CallObject(ipython_start_func, NULL); - } - } - - if (ipython_kernel == NULL || !PyCallable_Check(ipython_kernel)) { - goto error; - } - - goto cleanup; -error: - ipython_kernel = NULL; -cleanup: - Py_XDECREF(arglist); - Py_XDECREF(ipython_embed_module); - Py_XDECREF(ipython_start_func); - return ipython_kernel; -} - -void init_python(void) -{ - // Make sure the python is initialized - if (!Py_IsInitialized()) { - Py_Initialize(); - } -} - -void init_ipython_kernel(void) -{ - init_python(); - kernel_do_one_iteration = start_ipython_kernel(commandline_args); -} - -DWORD get_parent_pid() - { - static BOOL already_check_environment = FALSE; - static DWORD ppid = 0; - - if (TRUE == already_check_environment) { - return ppid; - } - - /* Get the environment variable for the parent pid */ - char pszPidString[30]; - DWORD ret = GetEnvironmentVariableA(PARENT_PID_ENV_NAME, pszPidString, sizeof(pszPidString)); - - already_check_environment = TRUE; - - if ((0 == ret) || (sizeof(pszPidString) == ret)) { - msg("No parent PID provided.\n"); - return 0; - } - - /* Parse it into a number and return it.*/ - OutputDebugStringA("Found parent PID"); - ppid = strtoul(pszPidString, NULL, 10); -} - -HANDLE get_parent_handle() - { - DWORD ppid = get_parent_pid(); - - if (0 == ppid) { - return NULL; - } - - HANDLE hParentProcess = OpenProcess(SYNCHRONIZE, FALSE, get_parent_pid()); - return hParentProcess; -} - -BOOL is_parent_dead() - { - int nArgs = 0; - - qstrvec_t out_args; - nArgs = parse_command_line3(GetCommandLineA(), &out_args, NULL, 0); - - - static HANDLE hParentProcess = NULL; - DWORD dwResult; - - if (NULL == hParentProcess) { - hParentProcess = get_parent_handle(); - } - - /* Still no parent handle? Well, it can't be dead then! */ - if (NULL == hParentProcess) { - return FALSE; - } - - dwResult = WaitForSingleObject(hParentProcess, 0); - if (WAIT_OBJECT_0 == dwResult) { - return TRUE; - } - - return FALSE; -} - -void ipython_embed_iteration() -{ - if (TRUE == is_parent_dead()) { - OutputDebugStringA("[IDA-IPython] Parent is dead. Terminating."); - ipython_embed_term(); - qexit(0); - } - - PyGILState_STATE state = PyGILState_Ensure(); - - if (kernel_do_one_iteration == NULL && !attempted_start_kernel) { - attempted_start_kernel = true; - init_ipython_kernel(); - //TODO: Report the error, call stack etc. - if ( PyErr_Occurred() ) { - msg("A Python Error Occurred trying to start the kernel!\n"); - } - } else if (kernel_do_one_iteration != NULL) { - PyObject_CallObject(kernel_do_one_iteration, NULL); - } - - PyGILState_Release(state); - -} - -FARPROC eventloop_address() -{ - HMODULE qtmodule = GetModuleHandleA(QT4_MODULE_NAME); - - if (NULL == qtmodule) { - qtmodule = GetModuleHandleA(QT5_MODULE_NAME); - } - - FARPROC src = GetProcAddress(qtmodule, EVENT_LOOP_FUNC_NAME); - return src; -} - -int __fastcall DetourQEventDispatcherWin32(void* ecx, void* edx, int i) -{ - try { - ipython_embed_iteration(); - return pQEventDispatcherWin32(ecx, edx, i); - } catch (const std::exception& ex) { - std::string error = ex.what(); - const char *cstr = error.c_str(); - warning(cstr); - } catch (...) { - warning("Something went wrong in the detour!"); - } - - return 0; -} - -void ipython_start_qtconsole() -{ - PyGILState_STATE state = PyGILState_Ensure(); - - PyObject *ipython_embed_module = NULL, - *ipython_qtconsole_func = NULL; - - ipython_embed_module = PyImport_ImportModule(IPYTHON_EMBED_MODULE); - if (ipython_embed_module == NULL) { - warning("could not import ipythonEmbed module"); - goto cleanup; - } - - ipython_qtconsole_func = PyObject_GetAttrString(ipython_embed_module, IPYTHON_EMBED_START_QTCONSOLE_METHOD_NAME); - if (ipython_qtconsole_func == NULL) { - warning("could not find start_qtconsole function"); - goto cleanup; - } - - if (!PyCallable_Check(ipython_qtconsole_func)) { - warning("ipython start_qtconsole function is not callable"); - goto cleanup; - } - - PyObject_CallObject(ipython_qtconsole_func, NULL); - -cleanup: - Py_XDECREF(ipython_embed_module); - Py_XDECREF(ipython_qtconsole_func); - - PyGILState_Release(state); -} - -IPYTHONEMBED_STATUS ipython_embed_start(PyObject* cmdline) -{ - commandline_args = cmdline; - - if (MH_Initialize() != MH_OK) { - return IPYTHONEMBED_MINHOOK_INIT_FAILED; - } - - void* qt_eventloop = (void*)eventloop_address(); - if (MH_CreateHook(qt_eventloop, - &DetourQEventDispatcherWin32, - (LPVOID*)&pQEventDispatcherWin32) != MH_OK) { - return IPYTHONEMBED_CREATE_HOOK_FAILED; - } - - if (MH_EnableHook(qt_eventloop) != MH_OK) { - return IPYTHONEMBED_ENABLE_HOOK_FAILED; - } - - return IPYTHONEMBED_OK; -} - -void ipython_embed_term() -{ - MH_DisableHook(MH_ALL_HOOKS); - MH_Uninitialize(); - Py_XDECREF(kernel_do_one_iteration); - Py_XDECREF(commandline_args); -} diff --git a/src/ipythonEmbed.h b/src/ipythonEmbed.h deleted file mode 100644 index 7f41ecc..0000000 --- a/src/ipythonEmbed.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef IPYTHONEMBED_H -#define IPYTHONEMBED_H - -#define _CRT_SECURE_NO_WARNINGS - -#include -#include -#include -#include - -#include "Windows.h" -#include "Python.h" -#include "MinHook.h" - -#include "pro.h" -#include "ida.hpp" -#include "idp.hpp" -#include "loader.hpp" -#include "expr.hpp" - - -typedef enum IPYTHONEMBED_STATUS -{ - IPYTHONEMBED_UNKNOWN = -1, - IPYTHONEMBED_OK = 0, - IPYTHONEMBED_ERROR, - IPYTHONEMBED_MINHOOK_INIT_FAILED, - IPYTHONEMBED_CREATE_HOOK_FAILED, - IPYTHONEMBED_ENABLE_HOOK_FAILED -} IPYTHONEMBED_STATUS; - -IPYTHONEMBED_STATUS ipython_embed_start(PyObject* cmdline); -void ipython_embed_term(); -void ipython_start_qtconsole(); - -#endif //IPYTHONEMBED_H diff --git a/src/persist.cpp b/src/persist.cpp deleted file mode 100644 index 4603a70..0000000 --- a/src/persist.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "persist.h" - -#include - -int persist(void) { - - BOOL bOk; - HMODULE hModule; - - /* Make sure the module stays in memory until process termination. */ - bOk = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN , (LPWSTR)persist, &hModule); - - if (0 == bOk) { - return -1; - } - - return 0; -} \ No newline at end of file diff --git a/src/persist.h b/src/persist.h deleted file mode 100644 index aa22332..0000000 --- a/src/persist.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -int persist(void); \ No newline at end of file diff --git a/src/proxy.py b/src/proxy.py deleted file mode 100644 index 12e7982..0000000 --- a/src/proxy.py +++ /dev/null @@ -1,19 +0,0 @@ -import win32process -import win32event -import sys - -args = '"{idaq_path}" "-Snothing.idc -f {connection_file}"' - -if __name__ == '__main__': - hProcess, hThread, dwProcessId, dwThreadId = win32process.CreateProcess(None, - args.format(idaq_path=sys.argv[1], - connection_file=sys.argv[2]), - None, - None, - 0, - 0, - None, - None, - win32process.STARTUPINFO()) - while win32event.WAIT_OBJECT_0 != win32event.WaitForSingleObject(hProcess, win32event.INFINITE): - pass