8

I'm writing an IDA plugin using idapython in order to add comments (located in database) to variables of struct types. In order to do this, firstly, i need to get the list of cross-references to a given structure type (e.g. struct BITMAPINFO) which can be found in "Structure" subview in IDA.

I know IDA provides this function from version 6.2 by right-button mouse clicking on the structure name and selecting "List cross references to". A window like the following will be popped-up:

example: xrefs to BITMAPINFO picture

Each item of the list in above picture is either an address where a global variable of type %structure name% (here is BITMAPINFO) is declared or a position where a local variable of type "structure name" is defined. The former is like

example: global variable definition of type GUID (here is type GUID, not BITMAPINFO).

The latter is like example: local variable definition of type BITMAPINFO This is the position where IDA declare local variables based on its identified type.

I wonder if there is a way to get these data by IDAPython.

NOTE: This is different from cross-references to a(ll) member(s) of a struct type, which can be got by right-button mouse clicking on the structure member name, shown as the following

example: XrefsTo GUID.DATA1

Before asking here, i do it like:

#CODE 1
ea = idc.LocByName(%structure name%)  

frm = [x.frm for x in idautils.XrefsTo(ea)]

I think i have got the whole list of cross-references to %structure name% using my above code. However, i found many EAs in the list seem like ineffective such as '0xff0052c9' (MaxEA is 0x108f800). However, i guess my code has got the desired result because the length of returned list is equal to the number of items in the list shown as the 1st picture. But i can't explain the result especially the (seemingly) ineffective ones. Also, when i add comments to the addresses in the list using the following code

#CODE 2
for ea in xrefs_list:
    # each cross-reference to the given struc type

    if repeatable:
        # add repeatable comment 'cmt' at address 'ea'
        idc.MakeRptCmt(ea, cmt)

    else:
        # add comment  'cmt' at address 'ea'
        idc.MakeComm(ea, cmt)

i found i only added comments to the effective addresses which are between idc.MinEA() and idc.MaxEA(), and these addresses are places where global instances of the queried struct type are declared, as shown in the 2nd picture.

My questions are:

  1. Is my above code (CODE 1) correct to get all cross-references to a struct type? If it is, how to explain those seemingly ineffective addresses (above 0xFF000000)
  2. How to add comments to other cross-reference addresses other than the references to global instances of the struct type?
  • Seems like those references are stack variable references. I updated my answer to match. – tmr232 Jun 5 '16 at 19:35
6

Stack Variable References

The xrefs you are showing are xrefs from stack variables. As you mentioned, when trying to get the xrefs to the struct, you get two types of results:

  1. Valid addresses, which are struct instances in the data section
  2. 0xFF000000 and above addresses, where the struct is used as a stack variable.

In IDA the stack variables for a function are represented internally as a struct, with each stack variable a member of that struct.

idaman struc_t *ida_export get_frame(const func_t *pfn);

Docs link

Knowing that, we can infer that the weird xrefs are member-ids (which they are). To get the names of the containing structs we use fullname = idaapi.get_member_fullname(mid) and get something like $ F4014B0.var_14. $ F4014B0 is the netnode name of the stack-frame struct for the function at 0xF4014B0.

import idc
import idaapi
import idautils

sid = idc.GetStrucIdByName('my-struct-name')
for xref in idautils.XrefsTo(sid):
    if 0xFF000000 > xref.frm:
        # The struct is used as data
        print 'Data xref from 0x{:X}'.format(xref.frm)
    else:
        # The struct is used as a stack variable.
        mid = xref.frm
        fullname = idaapi.get_member_fullname(mid)
        function_ea = int(fullname[2:].split('.')[0], 0x10)
        print 'Stack xref from 0x{:X}'.format(function_ea)

Getting Xrefs to member Variables

(This is here because it was my initial answer, and it contains valuable information.)

You were really close to the solution. Your code gives you the sid, so you only get references to the struct itself. What you need, is references to the different members, and this can be done using the mid. You can get it using fully-qualified member names. However, since there are many members to every struct, enumerating them is usually a better choice.

import idc
import idautils

def get_member_ids(sid):
    offset = 0
    while offset != 0xFFFFFFFF:
        mid = idc.GetMemberId(sid, offset)
        if mid != -1:
            yield mid
        offset = idc.GetStrucNextOff(sid, offset)


def get_member_xrefs(struc_name):
    sid = idc.GetStrucIdByName(struc_name)
    for mid in get_member_ids(sid):
        for xref in idautils.XrefsTo(mid):
            yield xref.frm


# And the usage:
for xref in get_member_xrefs('my-struct-name'):
    print '0x{:X}'.format(xref)

edit to answer question in comment

A Bit on Netnodes

This next bit is about IDA internals, so I might be off mark here. I am basic the information on SDK docs see here

As visible in my code, many APIs that are documented for use on effective addresses (eas) also work on other IDA primitives - struct ids, member ids - without any difference.

Internally, objects in IDA are mapped to netnodes. Lines in IDA are mapped linearly to netnode numbers, While other objects are mapped to numbers starting at 0xFF000000. While addresses and struct ids might have a different semantic meaning, they are both netnode numbers. In IDA's APIs, this means that they are treated the same way.

I hope this makes things clearer.

  • Thanks for your answer. What your code return is the union list of cross-references to each structure member, which can also be got by right-button mouse clicking on name of a structure member. This is different from the list of cross-references to %structure name% and isn't my desired result. I added new information to my question. Besides, I start to know SID and MID can be used as EA. Can you tell me how to explain this? – Bingchang Jun 5 '16 at 17:09
  • @Bingchang, I edited the reply to address your question about the MID and SID. I don't have an answer to the main question yet. I get the misunderstanding. – tmr232 Jun 5 '16 at 18:53
  • Your answer is really cool. Just as you answered, those (seemingly) ineffective addresses are references to stack variables and and each stack variable is a member of a struct which internally represents the stack frame of a function. I have solved my question and updated my question to include my solution. Besides, you open the door to internals of IDA. – Bingchang Jun 6 '16 at 7:42
  • @Bingchang I am happy to help. Just notice that your question does no longer match the title. You might want to split it into 2 separate questions for clarity, and so that people can look it up more easily in the future. – tmr232 Jun 6 '16 at 12:35
  • as you advised, i reedited my question and split it into 2 questions. However, i am not sure if it is suitable or if i need to open a new question. – Bingchang Jun 8 '16 at 2:59
1

In order to make the question and answer separate to make things clearer, I added my own solution mainly according to @tmr232's answer.

For the 1st question, just as @tmr232 answered, those (seemingly) ineffective addresses are references to stack variables. In IDA's internals, each stack variable is treated as a member of a struct which represents the stack frame of a function. CODE 1 in the question can return all cross-references to a struct type, including cross-references to global instances and local stack instances.

For the 2nd question, comments to be added to references to local stack instances can be added by treating these references as members of a struct.

The code is:

def add_struct_cmt (struct_name, cmt, repeatable):

    # locate ea by structure name, here, ea is identical to sid
    sid = idc.LocByName(%structure name%)

    if sid == idaapi.BADADDR:
        return 

    # get all cross-references to 'sid' including references to global 
    # struct instances and references to local stack variables
    frm = [x.frm for x in idautils.XrefsTo(sid)]

    for ea in frm:

        if ea > idc.MaxEA():
            # references to stack variables

            # IDA 6.8 and above: getting 'member_t' using 'ea' as mid
            mptr = idaapi.get_member_by_id(ea)

            # IDA 6.8: setting member comment using 'mptr' as index
            idaapi.set_member_cmt(mptr, cmt, repeatable)

            # IDA 6.9: 'mptr' is type of list
            #idaapi.set_member_cmt(mptr[0], cmt, repeatable)

        else:
            # references to global struct instances

            if repeatable:
                # add repeatable comment
                idc.MakeRptCmt(ea, cmt)

            else:
                # add non-repeatable comment
                idc.MakeCmt(ea, cmt)     
  • NOTE: 1. API get_member_by_id doesn't exist in IDA 6.6, however, this can be bypassed by other APIs, like sid = idaapi.get_member_struc(idaapi.get_member_fullname(mid)) and then enumerating its members – Bingchang Jun 8 '16 at 3:09
  • NOTE: in IDA 6.8, idaapi.get_member_by_id returns a proxy of type 'member_t *', however, in IDA 6.9, what it returns is a list and the proxy of type 'member_t *' is the 1st element. – Bingchang Jun 8 '16 at 3:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.