如何通过 IDAPython 在 IDA 中获取对结构类型的交叉引用并向结构类型的变量添加注释

逆向工程 艾达 蟒蛇
2021-06-29 05:48:54

我正在使用 idapython 编写一个 IDA 插件,以便向结构类型的变量添加注释(位于数据库中)。为了做到这一点,首先,我需要获取对给定结构类型(例如 struct BITMAPINFO)的交叉引用列表,该列表可以在 IDA 的“结构”子视图中找到。

我知道 IDA 从 6.2 版开始通过鼠标右键单击结构名称并选择“列出交叉引用”来提供此功能。将弹出如下窗口:

示例:外部参照到 BITMMAPINFO 图片

上图中的列表中的每一项要么是声明了 %structure name% 类型的全局变量(这里是 BITMAPINFO)的地址,要么是定义了“结构名”类型的局部变量的位置。前者就像

示例:GUID 类型的全局变量定义 (这里是类型 GUID,而不是 BITMPINFO)。

后者就像 示例:类型为 BITMAPIINFO 的局部变量定义 这是IDA根据其识别的类型声明局部变量的位置。

我想知道是否有办法通过IDAPython获取这些数据。

注意:这不同于对结构类型的(ll)个成员的交叉引用,可以通过鼠标右键单击结构成员名称来获得,如下所示

示例:XrefsTo GUID.DATA1

在问这里之前,我这样做:

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

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

我想我已经使用上面的代码获得了 %structure name% 的完整交叉引用列表。但是,我发现列表中的许多 EA 似乎无效,例如“0xff0052c9”(MaxEA 为 0x108f800)。但是,我想我的代码已经得到了想要的结果,因为返回列表的长度等于列表中的项目数,如第一张图片所示。但我无法解释结果,尤其是(看似)无效的结果。此外,当我使用以下代码向列表中的地址添加注释时

#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)

我发现我只在idc.MinEA()idc.MaxEA()之间的有效地址中添加了注释,这些地址是声明查询结构类型的全局实例的地方,如第二张图所示。

我的问题是:

  1. 我上面的代码(CODE 1)是否正确获取结构类型的所有交叉引用?如果是,如何解释那些看似无效的地址(0xFF000000以上)
  2. 除了对 struct 类型的全局实例的引用之外,如何向其他交叉引用地址添加注释?
2个回答

堆栈变量引用

您显示的外部参照是来自堆栈变量的外部参照。正如您所提到的,在尝试获取结构的外部参照时,您会得到两种类型的结果:

  1. 有效地址,即数据段中的结构体实例
  2. 0xFF000000 及以上地址,其中结构用作堆栈变量。

在 IDA 中,函数的堆栈变量在内部表示为一个结构体,每个堆栈变量都是该结构体的成员。

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

文档链接

知道了这一点,我们可以推断出奇怪的外部参照是成员 ID(它们是)。要获取我们使用的包含结构的名称fullname = idaapi.get_member_fullname(mid)并获得类似$ F4014B0.var_14. $ F4014B0是函数在 处的堆栈帧结构的网络节点名称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)

获取成员变量的外部参照

(这是因为它是我最初的回答,并且包含有价值的信息。)

你真的很接近解决方案。您的代码为您提供了sid,因此您只能获得对结构本身的引用。您需要的是对不同成员的引用,这可以使用mid. 您可以使用完全限定的成员名称来获取它。但是,由于每个结构都有许多成员,因此枚举它们通常是更好的选择。

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)

编辑以在评论中回答问题

网络节点上的一点

下一点是关于 IDA 内部的,所以我可能在这里不合时宜。我是关于 SDK 文档的基本信息,请参见此处

从我的代码中可以看出,许多被记录用于有效地址 ( eas) 的API也适用于其他 IDA 原语——结构 ID、成员 ID——没有任何区别。

在内部,IDA 中的对象被映射到网络节点。IDA 中的行线性映射到网络节点编号,而其他对象映射到从0xFF000000. 虽然地址和结构 ID 可能具有不同的语义,但它们都是网络节点编号。在 IDA 的 API 中,这意味着它们的处理方式相同。

我希望这能让事情更清楚。

为了把问题和答案分开,让事情更清楚,我主要根据@tmr232的答案添加了我自己的解决方案。

对于第一个问题,正如@tmr232 所回答的那样,那些(看似)无效的地址是对堆栈变量的引用。在 IDA 的内部结构中,每个堆栈变量都被视为表示函数堆栈帧的结构体的成员。问题中的 CODE 1可以返回对结构类型的所有交叉引用,包括对全局实例和本地堆栈实例的交叉引用。

对于第二个问题,可以通过将这些引用视为结构的成员来添加要添加到本地堆栈实例引用的注释。

代码是:

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)