from __future__ import absolute_import, division, print_function import os import sys import ctypes import traceback import idaapi import idc from ctypes import (c_int, c_void_p, create_string_buffer, cast) from PyQt5.QtCore import Qt, QTimer, QObject from PyQt5.QtGui import QShowEvent from PyQt5.QtWidgets import QWidget, QDialog, QDialogButtonBox, QPushButton, qApp __all__ = ['load_clr_file'] def _ida_lib(): ea_name = 'ida64' if idc.__EA64__ else 'ida' if sys.platform == 'win32': functype = ctypes.WINFUNCTYPE lib = ctypes.WinDLL(ea_name) elif sys.platform == 'darwin': functype = ctypes.CFUNCTYPE lib = ctypes.CDLL(idaapi.idadir("lib" + ea_name + ".dylib")) else: functype = ctypes.CFUNCTYPE lib = ctypes.CDLL('lib' + ea_name + '.so') return functype, lib functype, lib = _ida_lib() hook_cb_t = functype(c_void_p, c_void_p, c_int, c_void_p) hook_to_notification_point = lib.hook_to_notification_point hook_to_notification_point.argtypes = [c_int, hook_cb_t, c_void_p] unhook_from_notification_point = lib.unhook_from_notification_point unhook_from_notification_point.argtypes = [c_int, hook_cb_t, c_void_p] class TemporaryFilter(QObject): """ Temporary event filter installed at qApp to catch events while executing QDialog::exec. The filter automatically clicks &Import button, and automatically selects file by using native ui hooks. """ def __init__(self, filepath): super(TemporaryFilter, self).__init__() filepath = os.path.abspath(filepath) if not os.path.isfile(filepath): raise IOError("Assertion Error: os.path.isfile(filepath)") self.filepath = filepath def eventFilter(self, obj, event): def is_colors_dialog(): return isinstance( obj, QDialog) and 'IDA Colors' in obj.windowTitle() if isinstance(event, QShowEvent) and is_colors_dialog(): qApp.removeEventFilter(self) # Hide window and find &Import button obj.windowHandle().setOpacity(0) buttons = [widget for widget in obj.children() if isinstance( widget, QDialogButtonBox)][0] button = [widget for widget in buttons.buttons() if widget.text() == '&Import'][0] with NativeHook(ask_file=self.ask_file_handler): button.click() QTimer.singleShot(0, lambda: obj.accept()) return 1 return 0 def ask_file_handler(self): return create_string_buffer(self.filepath) class NativeHook: """ Installer for non-exposed hooks from UI_Hooks. This uses hook_to_notification_point with HT_UI. with NativeHook(ask_file=lambda: 0): # do anything """ NAMES = { 'ask_file': 0x1d } HT_UI = 1 def __init__(self, **kwargs): self.hooks = {NativeHook.NAMES[key]: value for key, value in kwargs.items()} self._handler = hook_cb_t(self.handler) def handler(self, _user_data, code, _va_args): if code in self.hooks: try: res = self.hooks[code]() return cast(res, c_void_p).value except: traceback.print_exc() return 0 else: return 0 def __enter__(self): hook_to_notification_point(NativeHook.HT_UI, self._handler, None) def __exit__(self, *args): unhook_from_notification_point(NativeHook.HT_UI, self._handler, None) def load_clr_file(filepath): event_filter = TemporaryFilter(filepath) qApp.installEventFilter(event_filter) return idaapi.process_ui_action('SetColors')