Skip to content

Commit

Permalink
Support for IDA 9.0 and other bug fixes.
Browse files Browse the repository at this point in the history
 * Add support for IDA 9.0, while maintaining backward compatibility.
 * Fix issue on IDA 9.0 where unpacking a structure would display a "?"
   in the Type column, and finalization of the structure would fail.
 * Add support to load the plugin on-demand by running HexRaysPyTools.py
   as a script from IDA, instead of installing the plugin.
 * Fix "A 32-bit module has been detected" crash when opening a 32-bit
   binary in 64-bit IDA.
 * Fix "Select Library" issue that prevents "Structures with this size"
   and "Select Containing Structure" features from working.
 * Fix multiple exceptions with "Select Containing Structure".
 * Prevent an exception if the structure declaration has errors.
 * Fix warning: Please use "widget_type" instead of "form_type".
  • Loading branch information
rohitab committed Aug 19, 2024
1 parent b8ebf75 commit 317f5a0
Show file tree
Hide file tree
Showing 12 changed files with 79 additions and 54 deletions.
7 changes: 7 additions & 0 deletions HexRaysPyTools.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,10 @@ def PLUGIN_ENTRY():
logging.root.setLevel(settings.DEBUG_MESSAGE_LEVEL)
idaapi.notify_when(idaapi.NW_OPENIDB, cache.initialize_cache)
return MyPlugin()


if __name__ == "__main__":
# Manually initialize plugin.
print("Initializing HexRaysPyTools Plugin")
plugin = PLUGIN_ENTRY()
plugin.init()
8 changes: 4 additions & 4 deletions HexRaysPyTools/callbacks/negative_offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def _parse_magic_comment(lvar):
structure_name, offset = m.group(1).split('+')
offset = int(offset)
parent_tinfo = idaapi.tinfo_t()
if parent_tinfo.get_named_type(idaapi.cvar.idati, structure_name) and parent_tinfo.get_size() > offset:
if parent_tinfo.get_named_type(None, structure_name) and parent_tinfo.get_size() > offset:
member_name = dict(find_deep_members(parent_tinfo, lvar.type().get_pointed_object())).get(offset, None)
if member_name:
return NegativeLocalInfo(lvar.type().get_pointed_object(), parent_tinfo, offset, member_name)
Expand Down Expand Up @@ -110,7 +110,7 @@ def find_containing_structures(self, type_library):
if not target_tinfo.get_named_type(type_library, self.tinfo.dstr()):
print("[Warning] Such type doesn't exist in '{0}' library".format(type_library.name))
return result
for ordinal in range(1, idaapi.get_ordinal_qty(type_library)):
for ordinal in range(1, helper.get_ordinal_qty(type_library)):
parent_tinfo.create_typedef(type_library, ordinal)
if parent_tinfo.get_size() >= min_struct_size:
for offset, name in find_deep_members(parent_tinfo, target_tinfo):
Expand Down Expand Up @@ -175,7 +175,7 @@ def create_containing_record(self, expression, index, offset):
new_cexpr_call.a.push_back(arg_field)
new_cexpr_call.thisown = False

parent = reversed(self.parents).next().cexpr
parent = next(reversed(self.parents)).cexpr

diff = negative_lvar.offset + offset
if diff:
Expand Down Expand Up @@ -220,7 +220,7 @@ def visit_expr(self, expression):
parent_name = expression.a[1].helper
member_name = expression.a[2].helper
parent_tinfo = idaapi.tinfo_t()
if not parent_tinfo.get_named_type(idaapi.cvar.idati, parent_name):
if not parent_tinfo.get_named_type(None, parent_name):
return 0
udt_data = idaapi.udt_type_data_t()
parent_tinfo.get_udt_details(udt_data)
Expand Down
4 changes: 2 additions & 2 deletions HexRaysPyTools/callbacks/new_field_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def activate(self, ctx):
udt_data.insert(iterator, helper.create_padding_udt_member(offset, idx))

struct_tinfo.create_udt(udt_data, idaapi.BTF_STRUCT)
struct_tinfo.set_numbered_type(idaapi.cvar.idati, ordinal, idaapi.BTF_STRUCT, struct_name)
struct_tinfo.set_numbered_type(None, ordinal, idaapi.BTF_STRUCT, struct_name)
hx_view.refresh_view(True)

@staticmethod
Expand All @@ -127,7 +127,7 @@ def parse_declaration(declaration):

_, tp, fld = result
tinfo = idaapi.tinfo_t()
tinfo.deserialize(idaapi.cvar.idati, tp, fld, None)
tinfo.deserialize(None, tp, fld, None)
if arr_size:
assert tinfo.create_array(tinfo, int(arr_size))
return tinfo, field_name
Expand Down
6 changes: 3 additions & 3 deletions HexRaysPyTools/callbacks/recasts.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ def activate(self, ctx):

elif isinstance(ri, RecastStructure):
tinfo = idaapi.tinfo_t()
tinfo.get_named_type(idaapi.cvar.idati, ri.structure_name)
ordinal = idaapi.get_type_ordinal(idaapi.cvar.idati, ri.structure_name)
tinfo.get_named_type(None, ri.structure_name)
ordinal = idaapi.get_type_ordinal(None, ri.structure_name)
if ordinal == 0:
return 0

Expand All @@ -196,7 +196,7 @@ def activate(self, ctx):
tinfo.get_udt_details(udt_data)
udt_data[idx].type = ri.recast_tinfo
tinfo.create_udt(udt_data, idaapi.BTF_STRUCT)
tinfo.set_numbered_type(idaapi.cvar.idati, ordinal, idaapi.NTF_REPLACE, ri.structure_name)
tinfo.set_numbered_type(None, ordinal, idaapi.NTF_REPLACE, ri.structure_name)
else:
raise NotImplementedError

Expand Down
2 changes: 1 addition & 1 deletion HexRaysPyTools/callbacks/scanners.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def activate(self, ctx):
NewDeepSearchVisitor(cfunc, 0, obj, cache.temporary_structure).process()

def update(self, ctx):
if ctx.form_type == idaapi.BWN_FUNCS:
if ctx.widget_type == idaapi.BWN_FUNCS:
idaapi.attach_action_to_popup(ctx.widget, None, self.name)
return idaapi.AST_ENABLE_FOR_WIDGET
return idaapi.AST_DISABLE_FOR_WIDGET
Expand Down
2 changes: 1 addition & 1 deletion HexRaysPyTools/callbacks/structs_by_size.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def activate(self, ctx):
operand_number = number_format_old.opnum
number_format_new.opnum = operand_number
number_format_new.props = number_format_old.props
number_format_new.type_name = idaapi.get_numbered_type_name(idaapi.cvar.idati, ordinal)
number_format_new.type_name = idaapi.get_numbered_type_name(None, ordinal)

c_function = hx_view.cfunc
number_formats = c_function.numforms # type: idaapi.user_numforms_t
Expand Down
23 changes: 12 additions & 11 deletions HexRaysPyTools/core/classes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from PyQt5 import QtCore, QtGui

import idaapi
import idc

import HexRaysPyTools.forms
from . import helper
Expand Down Expand Up @@ -65,7 +66,7 @@ def setData(self, column, value):
split = value.split('(')
if len(split) == 2:
value = split[0] + ' ' + self.name + '(' + split[1] + ';'
if idaapi.parse_decl(tinfo, idaapi.cvar.idati, value, idaapi.PT_TYP) is not None:
if idaapi.parse_decl(tinfo, None, value, idaapi.PT_TYP) is not None:
if tinfo.is_func():
tinfo.create_ptr(tinfo)
if tinfo.dstr() != self.tinfo.dstr():
Expand Down Expand Up @@ -110,7 +111,7 @@ def set_first_argument_type(self, name):
func_tinfo = self.tinfo.get_pointed_object()
class_tinfo = idaapi.tinfo_t()
if func_tinfo.get_func_details(func_data) and func_tinfo.get_nargs() and \
class_tinfo.get_named_type(idaapi.cvar.idati, name):
class_tinfo.get_named_type(None, name):
class_tinfo.create_ptr(class_tinfo)
first_arg_tinfo = func_data[0].type
if (first_arg_tinfo.is_ptr() and first_arg_tinfo.get_pointed_object().is_udt()) or \
Expand Down Expand Up @@ -175,7 +176,7 @@ def update(self):
if self.modified:
vtable_tinfo = idaapi.tinfo_t()
udt_data = idaapi.udt_type_data_t()
vtable_tinfo.get_numbered_type(idaapi.cvar.idati, self.ordinal)
vtable_tinfo.get_numbered_type(None, self.ordinal)
vtable_tinfo.get_udt_details(udt_data)
self.tinfo = vtable_tinfo
self.name = vtable_tinfo.dstr()
Expand All @@ -197,7 +198,7 @@ def update_local_type(self):
udt_member.type = virtual_function.tinfo
virtual_function.commit()
final_tinfo.create_udt(udt_data, idaapi.BTF_STRUCT)
final_tinfo.set_numbered_type(idaapi.cvar.idati, self.ordinal, idaapi.NTF_REPLACE, self.name)
final_tinfo.set_numbered_type(None, self.ordinal, idaapi.NTF_REPLACE, self.name)
self.modified = False
else:
print("[ERROR] Something have been modified in Local types. Please refresh this view")
Expand All @@ -219,11 +220,11 @@ def modified(self, value):

@staticmethod
def create(tinfo, class_):
ordinal = idaapi.get_type_ordinal(idaapi.cvar.idati, tinfo.dstr())
ordinal = idaapi.get_type_ordinal(None, tinfo.dstr())
if ordinal == 0:
if idaapi.import_type(idaapi.cvar.idati, -1, tinfo.dstr(), 0) == idaapi.BADNODE:
if idc.import_type(-1, tinfo.dstr()) == idaapi.BADNODE:
raise ImportError("unable to import type to idb ({})".format(tinfo.dstr()))
ordinal = idaapi.get_type_ordinal(idaapi.cvar.idati, tinfo.dstr())
ordinal = idaapi.get_type_ordinal(None, tinfo.dstr())

result = all_virtual_tables.get(ordinal)
if result:
Expand Down Expand Up @@ -291,7 +292,7 @@ def __init__(self, name, tinfo, ordinal):
@staticmethod
def create_class(ordinal):
tinfo = idaapi.tinfo_t()
tinfo.get_numbered_type(idaapi.cvar.idati, ordinal)
tinfo.get_numbered_type(None, ordinal)
vtables = {}
if tinfo.is_struct():
udt_data = idaapi.udt_type_data_t()
Expand Down Expand Up @@ -337,7 +338,7 @@ def update_local_type(self):
tinfo = idaapi.tinfo_t()
self.tinfo.get_udt_details(udt_data)
tinfo.create_udt(udt_data, idaapi.BTF_STRUCT)
tinfo.set_numbered_type(idaapi.cvar.idati, self.ordinal, idaapi.NTF_REPLACE, self.name)
tinfo.set_numbered_type(None, self.ordinal, idaapi.NTF_REPLACE, self.name)
self.modified = False

def set_first_argument_type(self, class_name):
Expand Down Expand Up @@ -388,7 +389,7 @@ def children(self):
@property
def tinfo(self):
tinfo = idaapi.tinfo_t()
tinfo.get_numbered_type(idaapi.cvar.idati, self.ordinal)
tinfo.get_numbered_type(None, self.ordinal)
return tinfo

def open_function(self):
Expand Down Expand Up @@ -450,7 +451,7 @@ def setupModelData(self, root):
all_virtual_tables.clear()

classes = []
for ordinal in range(1, idaapi.get_ordinal_qty(idaapi.cvar.idati)):
for ordinal in range(1, helper.get_ordinal_qty()):
result = Class.create_class(ordinal)
if result:
classes.append(result)
Expand Down
2 changes: 1 addition & 1 deletion HexRaysPyTools/core/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def init():
PX_WORD_TINFO, DUMMY_FUNC, CONST_PCHAR_TINFO, CHAR_TINFO, PCHAR_TINFO, CONST_VOID_TINFO, \
WORD_TINFO, PWORD_TINFO, EA64, EA_SIZE

EA64 = idaapi.get_inf_structure().is_64bit()
EA64 = idaapi.inf_is_64bit()
EA_SIZE = 8 if EA64 else 4

VOID_TINFO = idaapi.tinfo_t(idaapi.BT_VOID)
Expand Down
20 changes: 14 additions & 6 deletions HexRaysPyTools/core/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging

import idaapi
import ida_typeinf
import idc

import HexRaysPyTools.core.cache as cache
Expand All @@ -20,7 +21,7 @@ def is_imported_ea(ea):


def is_code_ea(ea):
if idaapi.cvar.inf.procname == "ARM":
if idaapi.inf_get_procname() == "ARM":
# In case of ARM code in THUMB mode we sometimes get pointers with thumb bit set
flags = idaapi.get_full_flags(ea & -2) # flags_t
else:
Expand All @@ -38,7 +39,7 @@ def get_ptr(ea):
if const.EA64:
return idaapi.get_64bit(ea)
ptr = idaapi.get_32bit(ea)
if idaapi.cvar.inf.procname == "ARM":
if idaapi.inf_get_procname() == "ARM":
ptr &= -2 # Clear thumb bit
return ptr

Expand All @@ -49,11 +50,18 @@ def get_ordinal(tinfo):
if ordinal == 0:
t = idaapi.tinfo_t()
struct_name = tinfo.dstr().split()[-1] # Get rid of `struct` prefix or something else
t.get_named_type(idaapi.cvar.idati, struct_name)
t.get_named_type(None, struct_name)
ordinal = t.get_ordinal()
return ordinal


def get_ordinal_qty(ti=None):
if idaapi.IDA_SDK_VERSION >= 900:
return ida_typeinf.get_ordinal_limit(ti)
else:
return idaapi.get_ordinal_qty(ti)


def get_virtual_func_addresses(name, tinfo=None, offset=None):
"""
Returns set of possible addresses of virtual function by its name.
Expand Down Expand Up @@ -175,7 +183,7 @@ def get_nice_pointed_object(tinfo):
name = tinfo.dstr()
if name[0] == 'P':
pointed_tinfo = idaapi.tinfo_t()
if pointed_tinfo.get_named_type(idaapi.cvar.idati, name[1:]):
if pointed_tinfo.get_named_type(None, name[1:]):
if tinfo.get_pointed_object().equals_to(pointed_tinfo):
return pointed_tinfo
except TypeError:
Expand Down Expand Up @@ -247,9 +255,9 @@ def import_structure(name, tinfo):
if idc.parse_decl(cdecl_typedef, idaapi.PT_TYP) is None:
return 0

previous_ordinal = idaapi.get_type_ordinal(idaapi.cvar.idati, name)
previous_ordinal = idaapi.get_type_ordinal(None, name)
if previous_ordinal:
idaapi.del_numbered_type(idaapi.cvar.idati, previous_ordinal)
idaapi.del_numbered_type(None, previous_ordinal)
ordinal = idaapi.idc_set_local_type(previous_ordinal, cdecl_typedef, idaapi.PT_TYP)
else:
ordinal = idaapi.idc_set_local_type(-1, cdecl_typedef, idaapi.PT_TYP)
Expand Down
8 changes: 5 additions & 3 deletions HexRaysPyTools/core/structure_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import idaapi
import idc

from . import helper

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -41,7 +43,7 @@ def name_and_color(self):
class StructureGraph:
# TODO:Enum types display
def __init__(self, ordinal_list=None):
self.ordinal_list = ordinal_list if ordinal_list else range(1, idc.get_ordinal_qty())
self.ordinal_list = ordinal_list if ordinal_list else range(1, helper.get_ordinal_qty())
self.local_types = {}
self.edges = []
self.final_edges = []
Expand Down Expand Up @@ -100,12 +102,12 @@ def get_tinfo_by_ordinal(ordinal):
if local_typestring:
p_type, fields = local_typestring
local_tinfo = idaapi.tinfo_t()
local_tinfo.deserialize(idaapi.cvar.idati, p_type, fields)
local_tinfo.deserialize(None, p_type, fields)
return local_tinfo
return None

def initialize_nodes(self):
for ordinal in range(1, idc.get_ordinal_qty()):
for ordinal in range(1, helper.get_ordinal_qty()):
# if ordinal == 15:
# import pydevd
# pydevd.settrace("localhost", port=12345, stdoutToServer=True, stderrToServer=True)
Expand Down
26 changes: 15 additions & 11 deletions HexRaysPyTools/core/temporary_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,16 +264,16 @@ def import_to_structures(self, ask=False):
cdecl_typedef = idaapi.ask_text(0x10000, cdecl_typedef, "The following new type will be created")
if not cdecl_typedef:
return
previous_ordinal = idaapi.get_type_ordinal(idaapi.cvar.idati, self.vtable_name)
previous_ordinal = idaapi.get_type_ordinal(None, self.vtable_name)
if previous_ordinal:
idaapi.del_numbered_type(idaapi.cvar.idati, previous_ordinal)
idaapi.del_numbered_type(None, previous_ordinal)
ordinal = idaapi.idc_set_local_type(previous_ordinal, cdecl_typedef, idaapi.PT_TYP)
else:
ordinal = idaapi.idc_set_local_type(-1, cdecl_typedef, idaapi.PT_TYP)

if ordinal:
print("[Info] Virtual table " + self.vtable_name + " added to Local Types")
return idaapi.import_type(idaapi.cvar.idati, -1, self.vtable_name)
return idc.import_type(-1, self.vtable_name)
else:
print("[Error] Failed to create virtual table " + self.vtable_name)
print("*" * 100)
Expand Down Expand Up @@ -413,7 +413,7 @@ def activate(self, temp_struct):
return
_, tp, fld = result
tinfo = idaapi.tinfo_t()
tinfo.deserialize(idaapi.cvar.idati, tp, fld, None)
tinfo.deserialize(None, tp, fld, None)
self.tinfo = tinfo
self.is_array = False

Expand Down Expand Up @@ -557,8 +557,12 @@ def pack(self, start=0, stop=None):
cdecl = idaapi.ask_text(0x10000, '#pragma pack(push, 1)\n' + cdecl, "The following new type will be created")

if cdecl:
structure_name = idaapi.idc_parse_decl(idaapi.cvar.idati, cdecl, idaapi.PT_TYP)[0]
previous_ordinal = idaapi.get_type_ordinal(idaapi.cvar.idati, structure_name)
decl = idaapi.idc_parse_decl(None, cdecl, idaapi.PT_TYP)
if not decl:
return

structure_name = decl[0]
previous_ordinal = idaapi.get_type_ordinal(None, structure_name)

if previous_ordinal:
reply = QtWidgets.QMessageBox.question(
Expand All @@ -568,15 +572,15 @@ def pack(self, start=0, stop=None):
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
)
if reply == QtWidgets.QMessageBox.Yes:
idaapi.del_numbered_type(idaapi.cvar.idati, previous_ordinal)
idaapi.del_numbered_type(None, previous_ordinal)
ordinal = idaapi.idc_set_local_type(previous_ordinal, cdecl, idaapi.PT_TYP)
else:
return
else:
ordinal = idaapi.idc_set_local_type(-1, cdecl, idaapi.PT_TYP)
if ordinal:
print("[Info] New type {0} was added to Local Types".format(structure_name))
tid = idaapi.import_type(idaapi.cvar.idati, -1, structure_name)
tid = idc.import_type(-1, structure_name)
if tid:
tinfo = idaapi.create_typedef(structure_name)
ptr_tinfo = idaapi.tinfo_t()
Expand Down Expand Up @@ -657,8 +661,8 @@ def get_recognized_shape(self, start=0, stop=-1):
return
min_size = enabled_items[-1].offset + enabled_items[-1].size - base
tinfo = idaapi.tinfo_t()
for ordinal in range(1, idaapi.get_ordinal_qty(idaapi.cvar.idati)):
tinfo.get_numbered_type(idaapi.cvar.idati, ordinal)
for ordinal in range(1, helper.get_ordinal_qty()):
tinfo.get_numbered_type(None, ordinal)
if tinfo.is_udt() and tinfo.get_size() >= min_size:
is_found = False
for offset in offsets:
Expand Down Expand Up @@ -740,7 +744,7 @@ def unpack_substructure(self, indices):
udt_data = idaapi.udt_type_data_t()
if item.tinfo.get_udt_details(udt_data):
for udt_item in udt_data:
member = Member(offset + udt_item.offset // 8, udt_item.type, None)
member = Member(offset + udt_item.offset // 8, udt_item.type.copy(), None)
member.name = udt_item.name
self.add_row(member)

Expand Down
Loading

0 comments on commit 317f5a0

Please sign in to comment.