idapython - 获取外部参照到堆栈变量

逆向工程 艾达 拆卸 蟒蛇 反汇编者 idapro-sdk
2021-07-09 08:01:25

我正在尝试编写一个 IDAPython 脚本,该脚本将返回对本地堆栈帧变量的引用列表。但是,我找不到任何这样做的 API。

我想要实现的是这样的代码: xrefs = get_variable_references('arg_4')它将返回与 GUI 的结果相对应的结果:

图形用户界面的结果

提前致谢。

2个回答

有一个函数可以执行此操作:build_stkvar_xrefs,在 C++ 中定义,但通过 Python SWIG 绑定公开。当您需要时,IDA 会动态构建堆栈外部参照。为了使用该功能,需要进行一些设置。

你需要使用一些函数来获得你需要的东西:

  • get_func(ea): 检索func_t函数结构ea
  • get_frame(func_t foo): 返回由struct_t指定的函数框架结构foo
  • DecodeInstruction(ea): 返回inst_t代表指令在ea
  • get_stkvar(op_t op, sval_t v):op是对指令的引用,v是操作数中的立即数。通常你只是使用op.addr. 它返回一个元组,(member_t, val)member_t是一个指向堆栈变量的指针,这正是我们所需要的。与堆栈val变量中的soff字段值相同member_t稍后会详细介绍。
  • xreflist_t(): 创建一个新xreflistxreflist_entry_t
  • build_stkvar_xrefs(xreflist_t xrefs, func_t func, member_t member): 用xreflist_entry_t表示memberin给出的堆栈 var 外部参照的's填充外部参照func
  • struct_t.get_member(x):您可以使用此方法迭代帧中的所有堆栈变量以检索所有member_t的。如果要为所有堆栈变量构建外部参照,这通常更容易。

这是所有这些如何联系在一起的示例:

# 0x4012d0 is the function address
# 0x4012dc is an instruction address referencing
# a stack variable. It looks like:
# mov [ebp - 4], ecx

pFunc = get_func(0x4012d0)
pFrame = get_frame(pFunc)
inst = DecodeInstruction(0x4012dc)
op = inst[0] #first operand references stack var
pMember, val = get_stkvar(op, op.addr)
xrefs = xreflist_t()
build_stkvar_xrefs(xrefs, pFunc, pMember)
for xref in xrefs:
    print hex(xref.ea) #print xref address

# Contrived member dictionary example.
dictMem = dict()
x = 0
while(x < pFrame.memqty):
    dictMem[GetMemberName(pFrame.id, pFrame.get_member(x).soff)] = pFrame.get_member(x)
    x = x+1
# given var name you can now use the
# dictionary to grab the member_t to pass
# to build_stkvar_xrefs
pMem = dictMem["var_4"]
xrefs = xreflist_t()
build_stkvar_xrefs(xrefs, pFunc, pMem)
for xref in xrefs:
    print hex(xref.ea) #print xrefs to var_4

soff不是堆栈偏移量。我认为它的意思是“结构偏移”,它是帧结构的偏移,因此您可以检索其他位信息。您将需要此字段来使用其他与堆栈变量相关的函数,例如:SetMemberType、SetMemberName、GetMemberName、DelStrucMember 等。

因此,对于外部参照查找的简单动态变量名称,您可以执行以下操作:

def get_stack_xrefs(func_ea, var_name):
    pFunc = get_func(func_ea)
    pFrame = get_frame(pFunc)
    pMember = None
    result = []
    while(x < pFrame.memqty):
        if GetMemberName(pFrame.id, pFrame.get_member(x).soff) == var_name:
           pMember = pFrame.get_member(x)
           break; 
        x = x+1
    if pMember: 
        xrefs = xreflist_t()
        build_stkvar_xrefs(xrefs, pFunc, pMember)
        for each in xrefs:
            result.append(each.ea)
    return result

如果您想了解有关这些功能的更多信息,我建议您查看 IDA SDK 文档中的以下模块(无特定顺序):

  • 功能文件
  • 框架.hpp
  • 结构体

参考:https : //www.hex-rays.com/products/ida/support/sdkdoc/files.html

TL;DR:没有简单的 API 来实现这一点,代码在答案的末尾或这里

据我所知,没有简单的方法来获取对堆栈结构的引用。似乎调用帧 ididautils.XrefsTo(sid)在哪里sid(使用 检索idc.GetFrame)应该可以工作,但是我无法在尝试中产生任何结果。

但是,您可以遍历函数的指令并在遇到堆栈引用操作数时手动计算堆栈中的偏移量。

我创建了一个要点片段来展示这一点,虽然我认为它很容易解释,但我会在这里简要介绍一下。

首先,我们需要堆栈偏移量到参数的映射,这由find_stack_members函数处理,它使用idc.GetFrame上面提到API 函数来获取特定函数堆栈的结构 ID。然后,它使用idautils.StructMembersAPI 迭代堆栈变量。

find_stack_members函数中一个有趣的逻辑是它使用<space>r成员名称作为堆栈的基础(一旦进入函数,堆栈就在这里),稍后用于find_stack_xrefs根据当前计算变量的堆栈偏移量堆栈增量和操作数立即值。

find_stack_xrefs函数迭代给定函数中的指令,并跳过任何指令,但那些定义为引用堆栈的操作数的指令(请注意,引用堆栈但未由 IDA 的自动分析或手动定义的参数将不会被处理,因此不会算作交叉引用)。

如果指令中存在堆栈偏移操作数,该find_stack_xrefs函数将继续使用堆栈的基偏移(之前由 检索find_stack_members)、当前堆栈增量(由 IDA 计算并使用 API 可用idc.GetSpd)和从操作数中获取的立即数来计算它的偏移结构体。

为方便起见,我还在此处包含了代码:

import idc, idaapi, idautils, ida_xref

def find_stack_members(func_ea):
    members = {}
    base = None
    frame = idc.GetFrame(func_ea)
    for frame_member in idautils.StructMembers(frame):
        member_offset, member_name, _ = frame_member
        members[member_offset] = member_name
        if member_name == ' r':
            base = member_offset
    if not base:
        raise ValueError("Failed identifying the stack's base address using the return address hidden stack member")
    return members, base

def find_stack_xrefs(func_offset):
    func_ea = ida_funcs.get_func(func_offset).startEA
    members, stack_base = find_stack_members(func_ea)
    for func_item in FuncItems(func_ea):
        flags = idc.GetFlags(ea)
        stkvar = 0 if idc.isStkvar0(flags) else 1 if idc.isStkvar1(flags) else None
        if not stkvar:
            continue
        ida_ua.decode_insn(func_item)
        op = ida_ua.cmd.Operands[stkvar]
        stack_offset = op.addr + idc.GetSpd(func_item) + stack_base
        member = members[stack_offset]
        print("At offset {:x} stack member {} is referenced by operand number {}".format(func_item, member, stkvar))

if __name__ == "__main__":
    find_stack_xrefs(idc.ScreenEA())