2

In developing a RESTful API for IDA >= 7.5 [https://github.com/sfinktah/idarest75], I have observed that a standard threaded webserver does not release it's socket when IDA terminates, unless it is run as a plugin. This behaviour may extend to all threading, I have not tested.

If the code below is simply loaded as a script (or pasted into the CLI) it will actually cause IDA to invisibly hang when IDA is exited, i.e. IDA appears to close but can actually found to be still running in Task Explorer -> Details.

Is there perhaps an IDA-specific atexit analog, as the builtin Python version certainly does not help.

Whilst the finished project is capable of running either as a plugin or a stand-alone script, the lack of any way to unload a python plugin requires keeping a reference to the class instance in order to call .term(), which is contra-indicated if one actually expects destruction to occur correctly.

Sample code (paste, exit IDA, observe running tasks).

def ida_issue():
    from http.server import BaseHTTPRequestHandler, HTTPServer
    from socketserver import ThreadingMixIn
    import threading

    class Handler(BaseHTTPRequestHandler):
        def do_GET(self):
            self.send_response(200)
            self.end_headers()

    class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
        allow_reuse_address = True

    class Worker(threading.Thread):
        def __init__(self, host, port):
            threading.Thread.__init__(self)
            self.httpd = ThreadedHTTPServer((host, port), Handler)
            self.host = host
            self.port = port

        def run(self):
            self.httpd.serve_forever()

        def stop(self):
            self.httpd.shutdown()
            self.httpd.server_close()

    class Master:
        def __init__(self):
            self.worker = Worker('127.0.0.1', 28612)
            self.worker.start()

    def main():
        master = Master()
        return master

    return main()

server = ida_issue()

How to re/unload [co-operative] IDAPython plugins.

class idarest_plugin_t(IdaRestConfiguration, ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_UNL

    def run(self, *args):
        pass

def reload_idarest():
    l = ida_loader.load_plugin(sys.modules['__plugins__idarest_plugin'].__file__)
    ida_load.run_plugin(l, 0)
    # pip install exectools (or ida_idaapi.require would probably suffice)
    unload('idarest_plugin')
    # reload plugin (or not)
    l = ida_loader.load_plugin(sys.modules['__plugins__idarest_plugin'].__file__)

1 Answer 1

2

Some research into the atexit module provided a simple solution. Create a plugin purely to trigger the atexit handlers when it receives a term.

import atexit
import idc
import ida_idaapi

class ida_atexit(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_UNL
    comment = "atexit"
    help = "triggers atexit._run_exitfuncs() when ida halts plugins"
    wanted_name = ""
    wanted_hotkey = ""

    def init(self):
        super(ida_atexit, self).__init__()
        return ida_idaapi.PLUGIN_KEEP

    def run(*args):
        pass

    def term(self):
        idc.msg('[ida_atexit::term] calling atexit._run_exitfuncs()\n')
        atexit._run_exitfuncs()

def PLUGIN_ENTRY():
    globals()['instance'] = ida_atexit()
    return globals()['instance']

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.