Skip to content

Porting an IDAPython Plugin to IDA 9

In IDA 9, the IDAPython API has been significantly upgraded, consolidating the ida_struct and ida_enum modules into the existing ida_typeinf module.

While this change simplifies the IDAPython API, it also breaks several IDAPython plugins, such as auto-enum and sark. This has caused some frustration among IDA users (1, 2).

Although HexRays has published an official IDAPython porting guide, it does not fully document all the changes to the IDAPython API. Additionally, as noted by @domenuk on X, several (many) blanks can be observed in the "alternatives" field.

This article documents my work in adapting the sark library for use with IDA 9. Thus, it does not (yet) completely document every single change that you need to make to be compatible with IDA 9. However, it does cover some of the common tasks and operations that are now no longer directly supported and how to re-implement them in IDA 9.

idaapi.get_inf_structure()

On IDA 8, this method returns a ida_ida.idainfo object which contains information about the current IDB.

This method has been removed in IDA 9. Instead, use the various idaapi.inf_get_*() methods.

Here are some examples:

IDA 8IDA 9
idaapi.get_inf_structure().procnameidaapi.inf_get_procname()
idaapi.get_inf_structure().max_eaidaapi.inf_get_max_ea()
idaapi.get_inf_structure().is_32bit()idaapi.inf_is_32bit_exactly()

Struct/enum error codes

Enum and struct related error codes (idaapi.ENUM_MEMBER_ERROR_* and idaapi.STRUC_ERROR_MEMBER_*) have been removed, along with the ida_enum and ida_struct modules.

Instead, methods such as idc.add_struc_member and idc.add_enum_member now return various ida_typeinf.TERR_* error codes.

As the error codes for enums and structs have been merged, there is no 1:1 mapping between the errors on IDA 8 and 9. Additionally, the error code values have changed between IDA 8 and 9 too.

Here's some examples:

IDA 8 NameIDA 8 ValueIDA 9 NameIDA 9 Value
ENUM_MEMBER_ERROR_NAME1TERR_DUPNAME or TERR_BAD_NAME-22 or -3
STRUC_ERROR_MEMBER_NAME1-1TERR_DUPNAME-22
STRUC_ERROR_MEMBER_OFFSET-2TERR_OVERLAP-14

1 I would expect this to return TERR_BAD_NAME as well, but apparently more characters are allowed in IDA struct member names now:

image-20240905085151841

which makes for some interesting pseudocode:

image-20240905085250188

ida_enum

This module has been removed entirely, with most of its methods moving to idc (refer to the official IDAPython porting guide). Thus, I will be focusing on methods that have been removed entirely or have undergone changes to their function signatures or semantics.

idc.add_enum

The first parameter, idx is still present, but no longer used. New enums will always be added to the end of the list of types, regardless of the idx passed.

idc.set_enum_member_cmt

The last parameter, repeatable, is still present, but no longer used. Enum member comments are now never repeatable.

idc.get_enum_cmt

The last parameter, repeatable, has been removed. However, the repeatable parameter of idc.set_enum_cmt is still present.

Methods relating to enum member serial

This includes get_enum_member_serial and idc.op_enum's final parameter, serial.

Prior to IDA 9, the serial field of an enum member was used to distinguish multiple enum members with the same value, but different name. For example, if MAP_ANON and MAP_ANONYMOUS both have the value of 0x20, they might receive serial 0 and 1 respectively.

In IDA 9, the serial field has been removed. Instead, the tid of an enum member is sufficient to uniquely identify that enum member.

To get the tid of the ith enum member of an enum identified by eid, use the get_type_by_tid and get_edm methods of ida_typeinf.tinfo_t:

python
def get_enum_member_tid(eid: int, i: int):
    tif = ida_typeinf.tinfo_t()
    tif.get_type_by_tid(eid)
    edm = ida_typeinf.edm_t()
    tif.get_edm(edm, i)
    return edm.get_tid()

get_enum_qty and getn_enum

These methods allowed for easy enumeration of all enums defined in the IDB. However, with the integration of enums into the unified types list, it seems the only way to iterate through all enums is to iterate through all types and test if that type is an enum:

python
def _iter_enum_ids():
    """Iterate the IDs of all enums in the IDB"""
    limit = ida_typeinf.get_ordinal_limit()
    for ordinal in range(1, limit):
        tif = ida_typeinf.tinfo_t()
        tif.get_numbered_type(None, ordinal)
        if tif.is_enum():
            yield tif.get_tid()

ida_struct

Similarly to ida_enum, this module also been completely removed, with most of its methods migrated to idc.Thus, I will be focusing on methods that have been removed entirely.

The struc_t and member_t types have been removed in favor of ida_typeinf.tinfo_t and ida_typeinf.udm_t.

get_struc

python
def get_struc(sid: int) -> Optional[ida_typeinf.tinfo_t]:
    tif = ida_typeinf.tinfo_t()
    if tif.get_type_by_tid(sid) and tif.is_udt():
	    return tif
    return None

get_member

python
def get_member(sid: int, offset: int) -> Optional[ida_typeinf.udm_t]:
    struc_tif = get_struc(sid)
    if struc_tif is None:
        return None
    udm = ida_typeinf.udm_t()
    udm.offset = offset
    idx = tif.find_udm(udm, ida_typeinf.STRMEM_AUTO)
    if idx != -1:
        return udm
    return None

get_member_by_name

python
def get_member_by_name(sid: int, name: str) -> Optional[ida_typeinf.udm_t]:
    struc_tif = get_struc(sid)
    if struc_tif is None:
        return None
    udm = ida_typeinf.udm_t()
    udm.name = name
    idx = tif.find_udm(udm, ida_typeinf.STRMEM_NAME)
    if idx != -1:
        return udm
    return None