2011-12-02 16:40:11 +01:00
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
# Standalone and testing code
|
|
|
|
import sys
|
|
|
|
try:
|
|
|
|
import pywraps
|
|
|
|
pywraps_there = True
|
|
|
|
print "Using pywraps"
|
|
|
|
except:
|
|
|
|
pywraps_there = False
|
|
|
|
print "Not using pywraps"
|
|
|
|
|
|
|
|
try:
|
|
|
|
import _idaapi
|
|
|
|
except:
|
|
|
|
print "Please try me from inside IDA"
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
import struct
|
|
|
|
|
|
|
|
if pywraps_there:
|
|
|
|
_idaapi.register_custom_data_type = pywraps.register_custom_data_type
|
|
|
|
_idaapi.unregister_custom_data_type = pywraps.unregister_custom_data_type
|
|
|
|
_idaapi.register_custom_data_format = pywraps.register_custom_data_format
|
|
|
|
_idaapi.unregister_custom_data_format = pywraps.unregister_custom_data_format
|
|
|
|
_idaapi.get_custom_data_format = pywraps.get_custom_data_format
|
|
|
|
_idaapi.get_custom_data_type = pywraps.get_custom_data_type
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
#<pycode(py_bytes)>
|
|
|
|
DTP_NODUP = 0x0001
|
|
|
|
|
|
|
|
class data_type_t(object):
|
|
|
|
"""
|
|
|
|
Custom data type definition. All data types should inherit from this class.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, name, value_size = 0, menu_name = None, hotkey = None, asm_keyword = None, props = 0):
|
|
|
|
"""Please refer to bytes.hpp / data_type_t in the SDK"""
|
|
|
|
self.name = name
|
|
|
|
self.props = props
|
|
|
|
self.menu_name = menu_name
|
|
|
|
self.hotkey = hotkey
|
|
|
|
self.asm_keyword = asm_keyword
|
|
|
|
self.value_size = value_size
|
|
|
|
|
|
|
|
self.id = -1 # Will be initialized after registration
|
|
|
|
"""Contains the data type id after the data type is registered"""
|
|
|
|
|
|
|
|
def register(self):
|
|
|
|
"""Registers the data type and returns the type id or < 0 on failure"""
|
|
|
|
return _idaapi.register_custom_data_type(self)
|
|
|
|
|
|
|
|
def unregister(self):
|
|
|
|
"""Unregisters the data type and returns True on success"""
|
|
|
|
# Not registered?
|
|
|
|
if self.id < 0:
|
|
|
|
return True
|
|
|
|
|
|
|
|
# Try to unregister
|
|
|
|
r = _idaapi.unregister_custom_data_type(self.id)
|
|
|
|
|
|
|
|
# Clear the ID
|
|
|
|
if r:
|
|
|
|
self.id = -1
|
|
|
|
return r
|
|
|
|
#<pydoc>
|
|
|
|
# def may_create_at(self, ea, nbytes):
|
|
|
|
# """
|
|
|
|
# (optional) If this callback is not defined then this means always may create data type at the given ea.
|
|
|
|
# @param ea: address of the future item
|
|
|
|
# @param nbytes: size of the future item
|
|
|
|
# @return: Boolean
|
|
|
|
# """
|
|
|
|
#
|
|
|
|
# return False
|
|
|
|
#
|
|
|
|
# def calc_item_size(self, ea, maxsize):
|
|
|
|
# """
|
|
|
|
# (optional) If this callback is defined it means variable size datatype
|
|
|
|
# This function is used to determine size of the (possible) item at 'ea'
|
|
|
|
# @param ea: address of the item
|
|
|
|
# @param maxsize: maximal size of the item
|
|
|
|
# @return: integer
|
|
|
|
# Returns: 0-no such item can be created/displayed
|
|
|
|
# this callback is required only for varsize datatypes
|
|
|
|
# """
|
|
|
|
# return 0
|
|
|
|
#</pydoc>
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
# Uncomment the corresponding callbacks in the inherited class
|
|
|
|
class data_format_t(object):
|
|
|
|
"""Information about a data format"""
|
|
|
|
def __init__(self, name, value_size = 0, menu_name = None, props = 0, hotkey = None, text_width = 0):
|
|
|
|
"""Custom data format definition.
|
|
|
|
@param name: Format name, must be unique
|
|
|
|
@param menu_name: Visible format name to use in menus
|
|
|
|
@param props: properties (currently 0)
|
|
|
|
@param hotkey: Hotkey for the corresponding menu item
|
|
|
|
@param value_size: size of the value in bytes. 0 means any size is ok
|
|
|
|
@text_width: Usual width of the text representation
|
|
|
|
"""
|
|
|
|
self.name = name
|
|
|
|
self.menu_name = menu_name
|
|
|
|
self.props = props
|
|
|
|
self.hotkey = hotkey
|
|
|
|
self.value_size = value_size
|
|
|
|
self.text_width = text_width
|
|
|
|
|
|
|
|
self.id = -1 # Will be initialized after registration
|
|
|
|
"""contains the format id after the format gets registered"""
|
|
|
|
|
|
|
|
def register(self, dtid):
|
|
|
|
"""Registers the data format with the given data type id and returns the type id or < 0 on failure"""
|
|
|
|
return _idaapi.register_custom_data_format(dtid, self)
|
|
|
|
|
|
|
|
def unregister(self, dtid):
|
|
|
|
"""Unregisters the data format with the given data type id"""
|
|
|
|
|
|
|
|
# Not registered?
|
|
|
|
if self.id < 0:
|
|
|
|
return True
|
|
|
|
|
|
|
|
# Unregister
|
|
|
|
r = _idaapi.unregister_custom_data_format(dtid, self.id)
|
|
|
|
|
|
|
|
# Clear the ID
|
|
|
|
if r:
|
|
|
|
self.id = -1
|
|
|
|
return r
|
|
|
|
#<pydoc>
|
|
|
|
# def printf(self, value, current_ea, operand_num, dtid):
|
|
|
|
# """
|
|
|
|
# Convert a value buffer to colored string.
|
|
|
|
#
|
|
|
|
# @param value: The value to be printed
|
|
|
|
# @param current_ea: The ea of the value
|
|
|
|
# @param operand_num: The affected operand
|
|
|
|
# @param dtid: custom data type id (0-standard built-in data type)
|
|
|
|
# @return: a colored string representing the passed 'value' or None on failure
|
|
|
|
# """
|
|
|
|
# return None
|
|
|
|
#
|
|
|
|
# def scan(self, input, current_ea, operand_num):
|
|
|
|
# """
|
|
|
|
# Convert from uncolored string 'input' to byte value
|
|
|
|
#
|
|
|
|
# @param input: input string
|
|
|
|
# @param current_ea: current address (BADADDR if unknown)
|
|
|
|
# @param operand_num: current operand number (-1 if unknown)
|
|
|
|
#
|
|
|
|
# @return: tuple (Boolean, string)
|
|
|
|
# - (False, ErrorMessage) if conversion fails
|
|
|
|
# - (True, Value buffer) if conversion succeeds
|
|
|
|
# """
|
|
|
|
# return (False, "Not implemented")
|
|
|
|
#
|
|
|
|
# def analyze(self, current_ea, operand_num):
|
|
|
|
# """
|
|
|
|
# (optional) Analyze custom data format occurrence.
|
|
|
|
# It can be used to create xrefs from the current item.
|
|
|
|
#
|
|
|
|
# @param current_ea: current address (BADADDR if unknown)
|
|
|
|
# @param operand_num: current operand number
|
|
|
|
# @return: None
|
|
|
|
# """
|
|
|
|
#
|
|
|
|
# pass
|
|
|
|
#</pydoc>
|
|
|
|
# -----------------------------------------------------------------------
|
2013-03-06 08:44:10 +01:00
|
|
|
def __walk_types_and_formats(formats, type_action, format_action, installing):
|
2011-12-02 16:40:11 +01:00
|
|
|
broken = False
|
|
|
|
for f in formats:
|
|
|
|
if len(f) == 1:
|
|
|
|
if not format_action(f[0], 0):
|
|
|
|
broken = True
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
dt = f[0]
|
|
|
|
dfs = f[1:]
|
2013-03-06 08:44:10 +01:00
|
|
|
# install data type before installing formats
|
|
|
|
if installing and not type_action(dt):
|
2011-12-02 16:40:11 +01:00
|
|
|
broken = True
|
|
|
|
break
|
2013-03-06 08:44:10 +01:00
|
|
|
# process formats using the correct dt.id
|
2011-12-02 16:40:11 +01:00
|
|
|
for df in dfs:
|
|
|
|
if not format_action(df, dt.id):
|
|
|
|
broken = True
|
|
|
|
break
|
2013-03-06 08:44:10 +01:00
|
|
|
# uninstall data type after uninstalling formats
|
|
|
|
if not installing and not type_action(dt):
|
|
|
|
broken = True
|
|
|
|
break
|
2011-12-02 16:40:11 +01:00
|
|
|
return not broken
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def register_data_types_and_formats(formats):
|
|
|
|
"""
|
|
|
|
Registers multiple data types and formats at once.
|
|
|
|
To register one type/format at a time use register_custom_data_type/register_custom_data_format
|
|
|
|
|
|
|
|
It employs a special table of types and formats described below:
|
|
|
|
|
|
|
|
The 'formats' is a list of tuples. If a tuple has one element then it is the format to be registered with dtid=0
|
|
|
|
If the tuple has more than one element, then tuple[0] is the data type and tuple[1:] are the data formats. For example:
|
|
|
|
many_formats = [
|
|
|
|
(pascal_data_type(), pascal_data_format()),
|
|
|
|
(simplevm_data_type(), simplevm_data_format()),
|
|
|
|
(makedword_data_format(),),
|
|
|
|
(simplevm_data_format(),)
|
|
|
|
]
|
|
|
|
The first two tuples describe data types and their associated formats.
|
|
|
|
The last two tuples describe two data formats to be used with built-in data types.
|
|
|
|
"""
|
|
|
|
def __reg_format(df, dtid):
|
|
|
|
df.register(dtid)
|
|
|
|
if dtid == 0:
|
2013-03-06 08:44:10 +01:00
|
|
|
print "Registered format '%s' with built-in types, ID=%d" % (df.name, df.id)
|
2011-12-02 16:40:11 +01:00
|
|
|
else:
|
2013-03-06 08:44:10 +01:00
|
|
|
print " Registered format '%s', ID=%d (dtid=%d)" % (df.name, df.id, dtid)
|
2011-12-02 16:40:11 +01:00
|
|
|
return df.id != -1
|
|
|
|
|
|
|
|
def __reg_type(dt):
|
|
|
|
dt.register()
|
2013-03-06 08:44:10 +01:00
|
|
|
print "Registered type '%s', ID=%d" % (dt.name, dt.id)
|
2011-12-02 16:40:11 +01:00
|
|
|
return dt.id != -1
|
2013-03-06 08:44:10 +01:00
|
|
|
ok = __walk_types_and_formats(formats, __reg_type, __reg_format, True)
|
2011-12-02 16:40:11 +01:00
|
|
|
return 1 if ok else -1
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
def unregister_data_types_and_formats(formats):
|
|
|
|
"""As opposed to register_data_types_and_formats(), this function
|
|
|
|
unregisters multiple data types and formats at once.
|
|
|
|
"""
|
|
|
|
def __unreg_format(df, dtid):
|
|
|
|
print "%snregistering format '%s'" % ("U" if dtid == 0 else " u", df.name)
|
2013-03-06 08:44:10 +01:00
|
|
|
df.unregister(dtid)
|
2011-12-02 16:40:11 +01:00
|
|
|
return True
|
|
|
|
|
|
|
|
def __unreg_type(dt):
|
|
|
|
print "Unregistering type '%s', ID=%d" % (dt.name, dt.id)
|
|
|
|
dt.unregister()
|
|
|
|
return True
|
2013-03-06 08:44:10 +01:00
|
|
|
ok = __walk_types_and_formats(formats, __unreg_type, __unreg_format, False)
|
2011-12-02 16:40:11 +01:00
|
|
|
return 1 if ok else -1
|
|
|
|
|
|
|
|
#</pycode(py_bytes)>
|
|
|
|
# -----------------------------------------------------------------------
|