Skip to content

Instantly share code, notes, and snippets.

@mabj
Forked from herrcore/HexCopy.py
Created June 6, 2020 14:32
Show Gist options
  • Save mabj/d36a31cf7e4deaebd66993403945959d to your computer and use it in GitHub Desktop.
Save mabj/d36a31cf7e4deaebd66993403945959d to your computer and use it in GitHub Desktop.
IDA Plugin for quickly copying disassembly as encoded hex bytes (updated for IDA 7xx)
############################################################################################
##
## One-Click Hex Copy!
##
## Updated for IDA 7.xx
##
## All credit for actual IOCTL decode logic:
## http://www.osronline.com/article.cfm?article=229
##
## Big thanks to @gaasedelen for the IDA 7 update ideas:
## https://github.com/gaasedelen/prefix/blob/master/plugin/ida_prefix.py
##
##
## To install:
## Copy script into plugins directory, i.e: C:\Program Files(x86)\<ida version>\plugins
##
## To run:
## Highlight disassembly instructions and right click and select "Hex Copy"
## The hex encoded bytes will be added to your clipboard
##
############################################################################################
__AUTHOR__ = '@herrcore'
PLUGIN_NAME = "Hex Copy"
PLUGIN_HOTKEY = 'Ctrl+Alt+c'
VERSION = '2.0'
import os
import idc
import idaapi
import idautils
major, minor = map(int, idaapi.get_kernel_version().split("."))
using_ida7api = (major > 6)
using_pyqt5 = using_ida7api or (major == 6 and minor >= 9)
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 copy_to_clip(data):
QApplication.clipboard().setText(data)
def PLUGIN_ENTRY():
"""
Required plugin entry point for IDAPython Plugins.
"""
return hex_copy()
class hex_copy(idaapi.plugin_t):
"""
The IDA Plugin for hex copy.
"""
flags = idaapi.PLUGIN_PROC | idaapi.PLUGIN_HIDE
comment = "Copy Hex Bytes"
help = "Highlight Assembly and right-click 'Copy Hex'"
wanted_name = PLUGIN_NAME
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_copy_bytes()
# 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.
"""
# unhook our plugin hooks
self._hooks.unhook()
# unregister our actions & free their resources
self._del_action_copy_bytes()
# done
idaapi.msg("%s terminated...\n" % self.wanted_name)
#--------------------------------------------------------------------------
# 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_COPY_BYTES = "prefix:copy_bytes"
def _init_action_copy_bytes(self):
"""
Register the copy bytes action with IDA.
"""
# describe the action
action_desc = idaapi.action_desc_t(
self.ACTION_COPY_BYTES, # The action name.
"Copy Hex", # The action text.
IDACtxEntry(copy_bytes), # The action handler.
None, # Optional: action shortcut
"Copy selected bytes as hex" # Optional: tooltip
)
# register the action with IDA
assert idaapi.register_action(action_desc), "Action registration failed"
def _del_action_copy_bytes(self):
"""
Delete the bulk prefix action from IDA.
"""
idaapi.unregister_action(self.ACTION_COPY_BYTES)
#------------------------------------------------------------------------------
# Plugin Hooks
#------------------------------------------------------------------------------
class Hooks(idaapi.UI_Hooks):
def finish_populating_widget_popup(self, widget, popup):
"""
A right click menu is about to be shown. (IDA 7)
"""
inject_hex_copy_actions(widget, popup, 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_hex_copy_actions(form, popup, idaapi.get_tform_type(form))
return 0
def hxe_callback(self, event, *args):
"""
HexRays event callback.
We lump this under the (UI) Hooks class for organizational reasons.
"""
#
# 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,
hex_copy.ACTION_COPY_BYTES,
"Copy Hex",
idaapi.SETMENU_APP
)
# done
return 0
#------------------------------------------------------------------------------
# Prefix Wrappers
#------------------------------------------------------------------------------
def inject_hex_copy_actions(form, popup, form_type):
"""
Inject prefix actions to popup menu(s) based on context.
"""
#
# disassembly window
#
if form_type == idaapi.BWN_DISASMS:
# insert the prefix action entry into the menu
#
idaapi.attach_action_to_popup(
form,
popup,
hex_copy.ACTION_COPY_BYTES,
"Copy Hex",
idaapi.SETMENU_APP
)
# done
return 0
#------------------------------------------------------------------------------
# Byte copy
#------------------------------------------------------------------------------
def copy_bytes():
"""
Copy selected bytes to clipboard
"""
if using_ida7api:
start = idc.read_selection_start()
end = idc.read_selection_end()
if idaapi.BADADDR in (start, end):
ea = idc.here()
start = idaapi.get_item_head(ea)
end = idaapi.get_item_end(ea)
data = idc.get_bytes(start, end - start).encode('hex')
print "Bytes copied: %s" % data
copy_to_clip(data)
else:
start = idc.SelStart()
end = idc.SelEnd()
if idaapi.BADADDR in (start, end):
ea = idc.here()
start = idaapi.get_item_head(ea)
end = idaapi.get_item_end(ea)
data = idc.GetManyBytes(start, end-start).encode('hex')
print "Bytes copied: %s" % data
copy_to_clip(data)
return
#------------------------------------------------------------------------------
# IDA ctxt
#------------------------------------------------------------------------------
class IDACtxEntry(idaapi.action_handler_t):
"""
A basic Context Menu class to utilize IDA's action handlers.
"""
def __init__(self, action_function):
idaapi.action_handler_t.__init__(self)
self.action_function = action_function
def activate(self, ctx):
"""
Execute the embedded action_function when this context menu is invoked.
"""
self.action_function()
return 1
def update(self, ctx):
"""
Ensure the context menu is always available in IDA.
"""
return idaapi.AST_ENABLE_ALWAYS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment