BaseHTTPRequestHandler + ThreadingMixin = 未关闭的端口

逆向工程 蟒蛇
2021-07-02 18:40:11

在为 IDA >= 7.5 [https://github.com/sfinktah/idarest75] 开发 RESTful API 时,我观察到标准线程 Web 服务器在 IDA 终止时不会释放它的套接字,除非它作为插件运行。这种行为可能会扩展到所有线程,我还没有测试过。

如果下面的代码只是作为脚本加载(或粘贴到 CLI 中),它实际上会导致 IDA 在 IDA 退出时无形地挂起,即 IDA 似乎已关闭但实际上可以在任务资源管理器 -> 详细信息中发现它仍在运行。

是否有特定于 IDA 的atexit模拟,因为内置的 Python 版本肯定没有帮助。

虽然完成的项目能够作为一个插件或独立脚本或者运行的,缺乏任何办法卸载一个Python插件需要为了保持对类实例的引用来调用.term(),这是禁忌,如果一个实际期望破坏正确发生。

示例代码(粘贴、退出 IDA、观察正在运行的任务)。

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()

如何重新/卸载 [合作] IDAPython 插件。

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个回答

对该atexit模块的一些研究提供了一个简单的解决方案。创建一个插件纯粹是为了atexit在收到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']