ida_hexrays.lvar_t.stk.get_stkoff() 不正确 ± 0x10

逆向工程 艾达 蟒蛇
2021-06-17 20:17:50

当使用堆栈偏移用于经由获得的可变ida_hexrays.lvar_t.stk.get_stkoff()ida_hexrays.vdloc_t.stkoff()结果“正确的”,和8或16之间变化作为手动测量字节出或相比于从所获得的结果:

id = idc.get_frame_id(here())
for member in idautils.StructMembers(id):
    offset, name, size = member

不正确的偏移量来自:

func = idaapi.get_func(ea)
vu = idaapi.open_pseudocode(func.start_ea, 0)
[n.get_stkoff() for n in vu.cfunc.lvars if n.is_stk_var()]

[编辑]:差异似乎是由伪代码 vu 报告相对于最小 SPD 的偏移量引起的,而其他一切都使用序言后的 SPD(下面的代码)

def GetPseudoStackOffsetCorrection(funcea):
    func = ida_funcs.get_func(funcea)
    return func.frsize + func.frregs + idc.get_spd(idc.get_min_spd_ea(func.start_ea))

[编辑 2]:这种替代方法有时有效它通过查看位于lvar_t.defea(假设“定义在 ea”)的 insn并从那里计算来起作用然而 10% 的时间lvar.defea点在条件 jmp 上,所以再次 - 非常笨拙

def get_stkoff_from_lvar(lvar, debug=1):
    ea = idc.get_item_head(lvar.defea)
    func = ida_funcs.get_func(ea)
    if not func:
        return idc.BADADDR
    
    for n in range(2):
        if idc.get_operand_type(ea, n) == idc.o_displ:
            offset = idc.get_operand_value(ea, n) + func.frsize - func.fpd

            if debug:
                lvar_name = lvar.name
                sid = idc.get_frame_id(func.start_ea)
                frame_name = idc.get_member_name(sid, offset)
                print("[debug] offset:0x{:x}, lvar_name:{}, frame_name:{}"
                        .format(offset, lvar_name, frame_name))

            return offset 

[编辑]:除了kludges(我不想使用)我的 [...]“重命名,重新键入和重新映射”(使用正则表达式)包有问题,我最近注意到它没有更新“堆栈”(在程序集中可见)调用时的名称,vu.rename_lvar就像通过执行重命名时通常发生的那样N

如果没有准确的偏移量,代码在重var_18命名伪代码变量v1(例如)时就无法重命名相应的堆栈变量

任何解决这个一般问题的方法都是受欢迎的,尽管优先考虑允许伪代码变量的正确堆栈位置的解决方案是最好的,因为我将很快通过flare-emu尝试“观察变量”。

这些是测试函数的一些结果,用于比较我收到的相互矛盾的结果,并向自己确认没有神奇的“只扣除 func.frregs”类型的答案。

功能 1.

func.flags                  : FUNC_FRAME | FUNC_PURGED_OK | FUNC_SP_READY
func.frregs                 :   8
func.frsize                 :  c0
func.fpd                    :  a0
ida_frame.frame_off_retaddr :  c8
ida_frame.frame_off_lvars   :   0
ida_frame.frame_off_args    :  d0
ida_frame.frame_off_savregs :  c0

name           lvar_offset1 stk_offset 
-------------- ------------ ---------- 
range          0x30         0x28       
guide          0x38         0x30       
ImageBase      0x58         0x50       
_stack_padding 0xd8         0xd0    

功能二

func.flags                  : FUNC_FRAME | FUNC_PURGED_OK | FUNC_SP_READY
func.frregs                 :   8
func.frsize                 : 1c0
func.fpd                    : 190
ida_frame.frame_off_retaddr : 1c8
ida_frame.frame_off_lvars   :   0
ida_frame.frame_off_args    : 1d0
ida_frame.frame_off_savregs : 1c0

name                      lvar_offset1 stk_offset 
------------------------- ------------ ---------- 
lpTopLevelExceptionFilter 0x50         0x40       
LibFileName               0x88         0x78       
Handle                    0xc8         0xb8       
lpProcName                0x110        0x100      
hObject                   0x198        0x188      
hModule                   0x1a0        0x190      

功能三

func.flags                  : FUNC_FRAME | FUNC_PURGED_OK | FUNC_SP_READY
func.frregs                 :   8
func.frsize                 :  40
func.fpd                    :  20
ida_frame.frame_off_retaddr :  48
ida_frame.frame_off_lvars   :   0
ida_frame.frame_off_args    :  50
ida_frame.frame_off_savregs :  40

name   lvar_offset1 stk_offset 
------ ------------ ---------- 
accum1 0x20         0x20       
accum2 0x2c         0x2c 

如果需要,我可以提供测试代码,虽然它不小。

[编辑:附上测试代码,警告:不漂亮]

# test code: 
#     sync_lvars_to_stk(func_ea)

def get_func_flag_names(f):
    return [x for x in [k for k in dir(idc) 
            if k.startswith('FUNC_')] 
            if f.flags & getattr(idc, x)]

def _get_vu(ea, vu):
    if vu: return vu
    return idaapi.open_pseudocode(idaapi.get_func(ea).start_ea, 0)

def get_lvars(ea, vu=None):
    vu = _get_vu(ea, vu)
    return [n.get_stkoff() for n in vu.cfunc.lvars if n.is_stk_var()]

def dump_stkvars(ea=None, iteratee=None):
    def get_member_tinfo(sid, offset):
        s = ida_struct.get_struc(sid)
        m = ida_struct.get_member(s, offset)
        tif = ida_typeinf.tinfo_t()
        try:
            if ida_struct.get_member_tinfo(tif, m):
                return tif
        except TypeError:
            pass

    results = []
    sid = idc.get_frame_id(ea)
    for member in idautils.StructMembers(sid):
        o = AttrDict()
        o.offset, o.name, o.size = member
        o.mid     = idc.get_member_id(sid,    o.offset)
        o.name    = idc.get_member_name(sid,  o.offset)
        o.size    = idc.get_member_size(sid,  o.offset)
        o.flags   = idc.get_member_flag(sid,  o.offset)
        tif       = get_member_tinfo(sid,     o.offset)
        o.tifname = str(tif) if tif else ''
        o.sid     = sid
        if callable(iteratee): iteratee(o)
        results.append(o)
    return results

def indexBy(o, key):
    r = {}
    for x in o:
        r[x[key]] = x
    return r

def sync_lvars_to_stk(ea, vu=None):
    vu = _get_vu(ea, vu)
    func = idaapi.get_func(ea)
    print("\n{:28}: {}\n{:28}: {:3x}\n{:28}: {:3x}\n{:28}: {:3x}\n{:28}: {:3x}\n{:28}: {:3x}\n{:28}: {:3x}\n{:28}: {:3x}\n"
            .format(
                "func.flags",                  " | ".join(get_func_flag_names(func)),
                "func.frregs",                 func.frregs,
                "func.frsize",                 func.frsize,
                "func.fpd",                    func.fpd,
                "ida_frame.frame_off_retaddr", ida_frame.frame_off_retaddr(func),
                "ida_frame.frame_off_lvars",   ida_frame.frame_off_lvars(func),
                "ida_frame.frame_off_args",    ida_frame.frame_off_args(func),
                "ida_frame.frame_off_savregs", ida_frame.frame_off_savregs(func),
                ))

    stkvars = indexBy(dump_stkvars(ea), 'name')
    lvars = []
    if vu and func:
        stk_lvars = [(n.name, n.tif.get_size(), 
            n.location.stkoff(),
            ) for n in vu.cfunc.lvars if n.location.is_stkoff()]

        for name, size, offset in stk_lvars:
            o = AttrDict()
            o.update({
                'name': name,
                'size': size,
                'lvar_offset': offset,
            })
            lvars.append(o)
    lvars = indexBy(lvars, 'name')

    lvar_names = lvars.keys()
    for name in lvar_names:
        if name in stkvars:
            print({
                'name': name,
                'lvar_offset': lvars[name].lvar_offset,
                'stk_offset': stkvars[name].offset,
            })

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

2个回答

我刚刚找到了官方版本的调整功能,用于将无用的基于 min-spd 的偏移量转换为基于天堂发送的帧的偏移量。

cfunc.get_stkoff_delta()

可能的实现:

def GetMinSpdAdjustment(funcea):
    func = ida_funcs.get_func(funcea)
    return 0 - (func.frsize + func.frregs + idc.get_spd(idc.get_min_spd_ea(func.start_ea)))

用法示例:

vu = idaapi.open_pseudocode(funcea, 0)
vu.cfunc.lvars[10].get_stkoff() - vu.cfunc.get_stkoff_delta()

完整示例:


# call rename_lvar(old, new, ea)

def get_pseudocode_vu(ea, vu):
    if vu:
        return vu
    
    return idaapi.open_pseudocode(ea, 0)

def label_stkvar(offset, name, ea=None, vu=None):
    sid = idc.get_frame_id(ea)
    old_name = idc.get_member_name(sid, offset)
    if old_name:
        if old_name == name:
            return old_name

        if idc.set_member_name(sid, offset, name):
            return old_name

def rename_lvar(old, new, ea, uniq=0, vu=None):
    def make_unique_name(name, taken):
        if name not in taken:
            return name
        fmt = "%s_%%i" % name
        for i in range(3, 1<<10):
            name = fmt % i
            if name not in taken:
                return name

    old = old.strip()
    new = new.strip()
    if old == new:
        return True

    vu = get_pseudocode_vu(ea, vu)
    names = [n.name for n in vu.cfunc.lvars]

    if new in names:
        if uniq:
            return False

        new = make_unique_name(new, names)

    lvars = [n for n in vu.cfunc.lvars if n.name == old]
    if lvars:
        lvar = lvars[0]
        if lvar.is_stk_var():
            offset = lvar.get_stkoff() - vu.cfunc.get_stkoff_delta()
            old_name = label_stkvar(offset, new, ea=ea, vu=vu)

        return vu.rename_lvar(lvar, new, 1)

根据我们在评论中讨论的信息,我有一个支持此类案例的想法。您可以使用idc.get_frame_regs_size(ea)来调整偏移量。因此可以执行以下操作:

adjust_offset = idc.get_frame_regs_size(ea)
[n.get_stkoff()+-adjust_offset for n in vu.cfunc.lvars if n.is_stk_var()]

根据您的实施。

您不妨考虑其他端点,例如 -

print(idc.get_frame_lvar_size(ea))
print(idc.get_frame_args_size(ea))
print(idc.get_frame_size(ea))

试验更多关于调整偏移。

我确信必须有一个强大的解决方案,这可能并不完美(我相信这在理论上应该可行)。但是,试一试。