Search code examples
pythonstructfunction-pointersreverse-engineeringida

Setting an IDA function pointer in a struct via script


The ordinary, manual way of redefining a struct member to become a function pointer would be to press on it, hit 'Y', and enter the proper declaration in the popup box. For example, for struct member fncQuery I would change the string to: BOOL (__cdecl *fncQuery)(char *cmdID)

This would be helpful; When I next identify a call to this function pointer, I would mark the appropriate "call [reg+offset]" line as this function pointer, and IDA will re-analyze and comment the parameters for me.

I have a thousand structs each with at least 1 such function pointer member, and corresponding lists of the descriptions of these functions' parameters and return values. Understandably, I want to match them up by an IDAPython script rather than by hand. However, I can't find an equivalent for the 'Y' button for scripts:

AddStrucMember does not cut it as far as I know - I can declare a member to become a pointer to a struct by giving it a struct ID, but that's not what I need.

SetMemberType is the same as the previous.

SetType requires a linear address and a type string - which would be perfect except linear addresses are exclusive to the code section, so I can't touch structure member definitions with SetType.

In my search I've found someone who conjured up dark magic, incorporating low level idaapi into his IDAPython script, to detect if a struct member has the same name as a known function, and if it does, "gets" the type of that function and "sets" it onto the member. Specifically, such horrible calls are seen (taken out of context of its definitions, you'll have to trust me when I say this runs properly and the first function call fills up its many outparameters with meaningful values):

get_named_type = g_dll.get_named_type
get_named_type.argtypes = [
    ctypes.c_void_p, #const til_t *ti,
    ctypes.c_char_p, #const char *name,
    ctypes.c_int, #int ntf_flags,
    ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte)), #const type_t **type=NULL,
    ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte)), #const p_list **fields=NULL,
    ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte)), #const char **cmt=NULL,
    ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte)), #const p_list **fieldcmts=NULL,
    ctypes.POINTER(ctypes.c_ulong), #sclass_t *sclass=NULL,
    ctypes.POINTER(ctypes.c_ulong), #uint32 *value=NULL);
]

get_named_type(
                til,
                funcname,
                idaapi.NTF_SYMM,
                ctypes.byref(typ_type),
                ctypes.byref(typ_fields),
                ctypes.byref(typ_cmt),
                ctypes.byref(typ_fieldcmts),
                ctypes.byref(typ_sclass),
                ctypes.byref(value)
type_arr = ctypes.create_string_buffer(0x400)
                type_arr[0] = chr(idaapi.BT_PTR)
                manualTypeCopy(type_arr, 1, len(type_arr), typ_type)
                ret = g_dll.set_member_tinfo(
                    til,
                    struc,
                    memb,
                    ctypes.c_uint(0),
                    type_arr,
                    typ_fields,
                    ctypes.c_uint(0),
                )

The work behind the scenes on "get_named_type" eludes me, and looking into its source (and fashioning something from it for my use) may be strongheaded and premature.

Do you know of an easier way my need can be fulfilled? I just need to define structure members as function pointers from an IDAPython script. Please help, thanks :)


Solution

  • It's old question, but I found a solution for others:

    SetType(idc.get_member_id(id, offset), "__int64 (__fastcall *)(ClassName *this)");
    
    • id - id of a structure (value returned by idc.add_struc, formerly AddStrucEx)
    • offset - is offset of a member of a structure, if you need change type by member name - get_member_offset (formerly GetMemberOffset) can be used for it

    For newer IDA/IDAPython versions refer to this porting guide