[IDA PRO] RVA 外掛

DirWangK發表於2024-06-10

IDA 外掛 - 跳轉到偏移量
IDA 外掛 - 獲取偏移量

Shift+G 跳轉到指定RVA
Ctrl+Shift+C (或右鍵選擇) 複製當前RVA到剪貼簿

#------------------------------------------------------------------------------
# IDA Plugin to jump to an offset from the Imagebase.
# Copy the 'cvutils-getoffset.py' into the plugins directory of IDA
#------------------------------------------------------------------------------

VERSION = '1.1.0'
__AUTHOR__ = 'cra0'


PLUGIN_HOTKEY = "Shift+G"
PLUGIN_NAME = "Go To Offset("+PLUGIN_HOTKEY+")"
COPY_PLUGIN_HOTKEY = "Ctrl+Shift+C"


  
import os
import sys
import idc
import idaapi
import idautils
import string


major, minor = map(int, idaapi.get_kernel_version().split("."))
using_ida7api = (major > 6)
using_pyqt5 = using_ida7api or (major == 6 and minor >= 9)

idaver_74newer = (major == 7 and minor >= 4)
idaver_8newer = (major >= 8)

if idaver_74newer or idaver_8newer:
    newer_version_compatible = True
else:
    newer_version_compatible = False

if newer_version_compatible:
    #IDA 7.4+
    #https://hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml
    import ida_ida
    import ida_kernwin
    
if using_pyqt5:
    import PyQt5.QtGui as QtGui
    import PyQt5.QtCore as QtCore
    import PyQt5.QtWidgets as QtWidgets
    from PyQt5.Qt import QApplication

else:
    import PySide.QtGui as QtGui
    import PySide.QtCore as QtCore
    QtWidgets = QtGui
    QtCore.pyqtSignal = QtCore.Signal
    QtCore.pyqtSlot = QtCore.Slot
    from PySide.QtGui import QApplication
    


def PLUGIN_ENTRY():
    """
    Required plugin entry point for IDAPython Plugins.
    """
    return cvutils_offset()

class cvutils_offset(idaapi.plugin_t):

    flags = idaapi.PLUGIN_PROC #| idaapi.PLUGIN_HIDE
    comment = "Go to an offset."
    help = "Use the shortcut key to open the goto dialog box."
    wanted_name = PLUGIN_NAME
    # wanted_hotkey = PLUGIN_HOTKEY
    wanted_hotkey=''

    #--------------------------------------------------------------------------
    # Plugin Overloads
    #--------------------------------------------------------------------------

    def init(self):
        """
        This is called by IDA when it is loading the plugin.
        """

        # initialize the menu actions our plugin will inject
        self._init_action_goto_offset()

# GET OFFSET
        # initialize the menu actions our plugin will inject
        self._init_action_get_offset()
        # initialize plugin hooks
        self._init_hooks()
        # done
        idaapi.msg("%s %s initialized...\n" % (self.wanted_name, VERSION))
        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        """
        This is called by IDA when this file is loaded as a script.
        """
        idaapi.msg("%s cannot be run as a script.\n" % self.wanted_name)

    def term(self):
        """
        This is called by IDA when it is unloading the plugin.
        """


        # unregister our actions & free their resources
        self._del_action_goto_offset()

# GET OFFSET
        # unhook our plugin hooks
        self._hooks.unhook()
        # unregister our actions & free their resources
        self._del_action_get_offset()

        # done
        idaapi.msg("%s terminated...\n" % self.wanted_name)

#  cvutils_getoffset
    #--------------------------------------------------------------------------
    # IDA Actions
    #--------------------------------------------------------------------------

    ACTION_GOTO_OFFSET  = "prefix:goto_offset"


    def _init_action_goto_offset(self):
        """
        Register the copy bytes action with IDA.
        """   
        vaction_desc = "Go to offset."
        if (sys.version_info > (3, 0)):
            # Describe the action using python3 copy
            action_desc = idaapi.action_desc_t(
                self.ACTION_GOTO_OFFSET,                                     # The action name.
                "Go to offset("+PLUGIN_HOTKEY+')',                                             # The action text.
                IDACtxEntry(goto_offset),                                # The action handler.
                PLUGIN_HOTKEY,                                              # Optional: action shortcut
                vaction_desc,                                               # Optional: tooltip
                31                                                          # Copy icon
            )
        else:
            # Describe the action using python2 copy
            action_desc = idaapi.action_desc_t(
                self.ACTION_GOTO_OFFSET,                                 # The action name.
                "Go to offset",                                         # The action text.
                IDACtxEntry(goto_offset),                            # The action handler.
                PLUGIN_HOTKEY,                                          # Optional: action shortcut
                vaction_desc,                                           # Optional: tooltip
                31                                                      # Copy icon
            )


        # register the action with IDA
        assert idaapi.register_action(action_desc), "Action registration failed"


    def _del_action_goto_offset(self):
        """
        Delete the bulk prefix action from IDA.
        """
        idaapi.unregister_action(self.ACTION_GOTO_OFFSET)
    #--------------------------------------------------------------------------
    # Plugin Hooks
    #--------------------------------------------------------------------------

    def _init_hooks(self):
        """
        Install plugin hooks into IDA.
        """
        self._hooks = Hooks()
        self._hooks.ready_to_run = self._init_hexrays_hooks
        self._hooks.hook()

    def _init_hexrays_hooks(self):
        """
        Install Hex-Rrays hooks (when available).
        NOTE: This is called when the ui_ready_to_run event fires.
        """
        if idaapi.init_hexrays_plugin():
            idaapi.install_hexrays_callback(self._hooks.hxe_callback)

    #--------------------------------------------------------------------------
    # IDA Actions
    #--------------------------------------------------------------------------

    ACTION_GET_OFFSET  = "prefix:get_offset"


    def _init_action_get_offset(self):
        """
        Register the get offset action with IDA.
        """   
        # If the action is already registered, unregister it first.
        if idaapi.unregister_action(self.ACTION_GET_OFFSET):
            idaapi.msg("Warning: action was already registered, unregistering it first\n")
        
        vaction_desc = "Get the offset from the image base of the current cursor address."
        if (sys.version_info > (3, 0)):
            # Describe the action using python3 copy
            action_desc = idaapi.action_desc_t(
                self.ACTION_GET_OFFSET,                                     # The action name.
                "Get Offset("+COPY_PLUGIN_HOTKEY+')',                                               # The action text.
                IDACtxEntry(getcopy_offset),                                # The action handler.
                COPY_PLUGIN_HOTKEY,                                              # Optional: action shortcut
                vaction_desc,                                               # Optional: tooltip
                31                                                          # Copy icon
            )
        else:
            # Describe the action using python2 copy
            action_desc = idaapi.action_desc_t(
                self.ACTION_GET_OFFSET,                                 # The action name.
                "Get Offset("+COPY_PLUGIN_HOTKEY+')',                                           # The action text.
                IDACtxEntry(getcopy_offset),                            # The action handler.
                COPY_PLUGIN_HOTKEY,                                          # Optional: action shortcut
                vaction_desc,                                           # Optional: tooltip
                31                                                      # Copy icon
            )

        # register the action with IDA
        assert idaapi.register_action(action_desc), "Action registration failed"


    def _del_action_get_offset(self):
        """
        Delete the bulk prefix action from IDA.
        """
        idaapi.unregister_action(self.ACTION_GET_OFFSET)
#------------------------------------------------------------------------------
# Display a warning message box
#------------------------------------------------------------------------------
def display_warning(message): 
    if newer_version_compatible:
        return idaapi.warning(message)
    else:
        return idc.Warning(message)
   
#------------------------------------------------------------------------------
# Jump to a certain address
#------------------------------------------------------------------------------
def jump_to_address(jump_address): 
    if newer_version_compatible:
        ida_kernwin.jumpto(jump_address)
    else:
        idc.Jump(jump_address)


#------------------------------------------------------------------------------
# Image Min EA
#------------------------------------------------------------------------------
def get_minEA(): 
    if newer_version_compatible:
        return ida_ida.inf_get_min_ea()
    else:
        return idc.MinEA()

#------------------------------------------------------------------------------
# IsBadAddress
#------------------------------------------------------------------------------

def is_hex(s):
     hex_digits = set(string.hexdigits)
     # if s is long, then it is faster to check against a set
     return all(c in hex_digits for c in s)

def is_hex(s):
    try:
        int(s, 16)
        return True
    except ValueError:
        return False

def isvalid_address(ea):
    """Check if the given address is valid
    
    Arguments:
        ea: The linear address to check.
    """   
    if (ea == idaapi.BADADDR):
        print("[%x] BADADDR" % ea)
        return 0
        
    pe_min_ea = get_minEA()
       
    if (ea < pe_min_ea):
        print("[%x] is lower than MinEA [%x]" % (ea, pe_min_ea))
        return 0
               
    if not idaapi.getseg(ea):
        print("[%x] getseg failed" % ea)
        return 0
    
    return 1


#------------------------------------------------------------------------------
# Go to offset
#------------------------------------------------------------------------------

def goto_offset():
    hint_min_offset = get_minEA() - idaapi.get_imagebase()
    # print('hint_min_offset:',hint_min_offset)
    #Use string for now, force them to use hex as the offset
    offset_value=None
    if newer_version_compatible:
        offset_value = ida_kernwin.ask_str("0x%x" % hint_min_offset, 0, "To Offset[HEX]:")    
    else:
        offset_value = idc.AskStr("0x%x" % hint_min_offset, "To Offset[HEX]:")

    if not offset_value:
        print("No value was provided. Ignoring")
        return
    
    if not is_hex(offset_value):
        print("Bad input; It doesn't contain valid hex")
        display_warning("Bad Input!")
        return
        
    offset_hex = int(offset_value, 16)
    if offset_hex == 0:
        display_warning("Input is Invalid!")
        return
       
    image_base = idaapi.get_imagebase()
    jump_address = image_base + offset_hex
    
    if (isvalid_address(jump_address)):
        print ("Offset [%x] =-> Address [0x%x]" % (offset_hex, jump_address))
        jump_to_address(jump_address)
    else:
        display_warning("Bad Offset!")
    
    return

#------------------------------------------------------------------------------
# IDA ctxt
#------------------------------------------------------------------------------

class IDACtxEntry(idaapi.action_handler_t):

    def __init__(self, action_function):
        idaapi.action_handler_t.__init__(self)
        self.action_function = action_function

    def activate(self, ctx):
        self.action_function()
        return 1

    def update(self, ctx):
        return idaapi.AST_ENABLE_ALWAYS

#------------------------------------------------------------------------------
# Plugin Hooks
#------------------------------------------------------------------------------

class Hooks(idaapi.UI_Hooks):

    def __init__(self):
        # Call the __init__ method of the superclass
        super(Hooks, self).__init__()

        # Get the IDA version
        major, minor = map(int, idaapi.get_kernel_version().split("."))
        self.newer_version_compatible = (major == 7 and minor >= 4) or (major >= 8)
        
        # If the IDA version is less than 7.4, define finish_populating_tform_popup
        if not self.newer_version_compatible:
            self.finish_populating_tform_popup = self._finish_populating_tform_popup

    def finish_populating_widget_popup(self, widget, popup_handle, ctx=None):
        """
        A right click menu is about to be shown. (IDA 7.x)
        """
        inject_address_offset_copy_actions(widget, popup_handle, idaapi.get_widget_type(widget))
        return 0


    def _finish_populating_tform_popup(self, form, popup):
        """
        A right click menu is about to be shown. (IDA 6.x)
        """
        inject_address_offset_copy_actions(form, popup, idaapi.get_tform_type(form))
        return 0

    def hxe_callback(self, event, *args):
        """
        HexRays event callback.
        """

        #
        # if the event callback indicates that this is a popup menu event
        # (in the hexrays window), we may want to install our prefix menu
        # actions depending on what the cursor right clicked.
        #

        if event == idaapi.hxe_populating_popup:
            form, popup, vu = args

            idaapi.attach_action_to_popup(
                form,
                popup,
                cvutils_offset.ACTION_GET_OFFSET,
                "Get Address Offset",
                idaapi.SETMENU_APP,
            )

        # done
        return 0

#------------------------------------------------------------------------------
# Prefix Wrappers
#------------------------------------------------------------------------------

def inject_address_offset_copy_actions(widget, popup_handle, widget_type):
    if widget_type == idaapi.BWN_DISASMS:
        idaapi.attach_action_to_popup(
            widget,
            popup_handle,
            cvutils_offset.ACTION_GET_OFFSET,
            "Get Address Offset",
            idaapi.SETMENU_APP
        )
    return 0

#------------------------------------------------------------------------------
# Get Screen linear address
#------------------------------------------------------------------------------
def get_screen_linear_address(): 
    if newer_version_compatible:
        return idc.get_screen_ea()
    else:
        return idc.ScreenEA()


#------------------------------------------------------------------------------
# Get Offset
#------------------------------------------------------------------------------
def setClipboardText(data):
    cb = QApplication.clipboard()
    cb.clear(mode=cb.Clipboard )
    cb.setText(data, mode=cb.Clipboard)

def getcopy_offset():
    """
    Gets the offset of the current cursor's address
    """
    vImagebase = idaapi.get_imagebase()
    vCurrentPos = get_screen_linear_address()
    if vCurrentPos != idaapi.BADADDR:
        vOffset = vCurrentPos - vImagebase
        print ("Address [0x%x] =-> Offset [%x] Copied to Clipboard!" % (vCurrentPos, vOffset))
        setClipboardText("%x" % vOffset)
    return

相關文章