Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions plugins/patching.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@

# this plugin requires IDA 7.6 or newer
try:
import idaapi
import ida_pro
import ida_idaapi
IDA_GLOBAL_SCOPE = sys.modules['__main__']
SUPPORTED_IDA = ida_pro.IDA_SDK_VERSION >= 760
except:
Expand All @@ -48,7 +48,7 @@ def PLUGIN_ENTRY():
"""
return PatchingPlugin()

class PatchingPlugin(ida_idaapi.plugin_t):
class PatchingPlugin(idaapi.plugin_t):
"""
The IDA Patching plugin stub.
"""
Expand All @@ -60,7 +60,7 @@ class PatchingPlugin(ida_idaapi.plugin_t):
# - PLUGIN_UNL: Unload the plugin after calling run()
#

flags = ida_idaapi.PLUGIN_PROC | ida_idaapi.PLUGIN_HIDE | ida_idaapi.PLUGIN_UNL
flags = idaapi.PLUGIN_PROC | idaapi.PLUGIN_HIDE | idaapi.PLUGIN_UNL
comment = "A plugin to enable binary patching in IDA"
help = ""
wanted_name = "Patching"
Expand All @@ -78,16 +78,24 @@ def init(self):
This is called by IDA when it is loading the plugin.
"""
if not SUPPORTED_ENVIRONMENT or self.__updated:
return ida_idaapi.PLUGIN_SKIP
return idaapi.PLUGIN_SKIP

# load the plugin core
self.core = patching.PatchingCore(defer_load=True)

# inject a reference to the plugin context into the IDA console scope
IDA_GLOBAL_SCOPE.patching = self

addon = idaapi.addon_info_t()
addon.id = "github.gaasedelen.patching"
addon.name = "Patching"
addon.producer = "Markus Gaasedelen"
addon.url = "https://github.com/gaasedelen/patching"
addon.version = "0.3.0.0"
idaapi.register_addon(addon)

# mark the plugin as loaded
return ida_idaapi.PLUGIN_KEEP
return idaapi.PLUGIN_KEEP

def run(self, arg):
"""
Expand Down
4 changes: 2 additions & 2 deletions plugins/patching/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@
class PatchingCore(object):

PLUGIN_NAME = 'Patching'
PLUGIN_VERSION = '0.2.0'
PLUGIN_VERSION = '0.3.0'
PLUGIN_AUTHORS = 'Markus Gaasedelen'
PLUGIN_DATE = '2024'
PLUGIN_DATE = '2025'

def __init__(self, defer_load=False):

Expand Down
14 changes: 10 additions & 4 deletions plugins/patching/util/ida.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,10 @@ def attach_submenu_to_popup(popup_handle, submenu_name, prev_action_name):

# cast an IDA 'popup handle' pointer back to a QMenu object
p_qmenu = ctypes.cast(int(popup_handle), ctypes.POINTER(ctypes.c_void_p))[0]
qmenu = sip.wrapinstance(int(p_qmenu), QtWidgets.QMenu)
if ida_pro.IDA_SDK_VERSION >= 920:
qmenu = shiboken6.wrapInstance(int(p_qmenu), QtWidgets.QMenu)
else:
qmenu = sip.wrapinstance(int(p_qmenu), QtWidgets.QMenu)

# create a Qt (sub)menu that can be injected into an IDA-originating menu
submenu = QtWidgets.QMenu(submenu_name)
Expand Down Expand Up @@ -893,21 +896,24 @@ def remove_ida_actions(popup):

class FilterMenu(QtCore.QObject):
def __init__(self, qmenu):
super(QtCore.QObject, self).__init__()
super(FilterMenu, self).__init__()
self.qmenu = qmenu

def eventFilter(self, obj, event):
if event.type() != QtCore.QEvent.Polish:
return False
for action in self.qmenu.actions():
if action.text() in ["&Font...", "&Synchronize with"]: # lol..
qmenu.removeAction(action)
self.qmenu.removeAction(action)
self.qmenu.removeEventFilter(self)
self.qmenu = None
return True

p_qmenu = ctypes.cast(int(popup), ctypes.POINTER(ctypes.c_void_p))[0]
qmenu = sip.wrapinstance(int(p_qmenu), QtWidgets.QMenu)
if ida_pro.IDA_SDK_VERSION >= 920:
qmenu = shiboken6.wrapInstance(int(p_qmenu), QtWidgets.QMenu)
else:
qmenu = sip.wrapinstance(int(p_qmenu), QtWidgets.QMenu)
filter = FilterMenu(qmenu)
qmenu.installEventFilter(filter)

Expand Down
47 changes: 34 additions & 13 deletions plugins/patching/util/qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,44 @@

QT_AVAILABLE = False

# attempt to load PyQt5
try:
import PyQt5.QtGui as QtGui
import PyQt5.QtCore as QtCore
import PyQt5.QtWidgets as QtWidgets
from PyQt5 import sip

# importing PyQt5 went okay, let's see if we're in an IDA Qt context
import ida_pro

if ida_pro.IDA_SDK_VERSION >= 920:
# attempt to load PySide6
try:
import ida_kernwin
QT_AVAILABLE = ida_kernwin.is_idaq()
import PySide6.QtGui as QtGui
import PySide6.QtCore as QtCore
import PySide6.QtWidgets as QtWidgets
import shiboken6

# importing PySide6 went okay, let's see if we're in an IDA Qt context
try:
import ida_kernwin
QT_AVAILABLE = ida_kernwin.is_idaq()
except ImportError:
pass

# import failed, PySide6 is not available
except ImportError:
pass
else:
# attempt to load PyQt5
try:
import PyQt5.QtGui as QtGui
import PyQt5.QtCore as QtCore
import PyQt5.QtWidgets as QtWidgets
from PyQt5 import sip

# import failed, PyQt5 is not available
except ImportError:
pass
# importing PyQt5 went okay, let's see if we're in an IDA Qt context
try:
import ida_kernwin
QT_AVAILABLE = ida_kernwin.is_idaq()
except ImportError:
pass

# import failed, PyQt5 is not available
except ImportError:
pass

#--------------------------------------------------------------------------
# Qt Misc Helpers
Expand Down