如何从 IDA 反汇编中删除 OFF64 SEGDEF

逆向工程 艾达
2021-06-28 19:02:20

这一直困扰着我从 IDA 6.5 到 7.5。

.text:0000000143EFE8CD CC                     db 0CCh ; Ì OFF64 SEGDEF [_text,140CFC35B]

它可能看起来不多,但是当条件跳转被放置在这样的位置上时,结果是令人虚弱的。

.text:0000000143EFE8B6 0F 8F 9F DA DF FC      jg      loc_143019000-231CCA5h

每个实例都会xref在 0x143019000 处创建一个到第三个 .text 段以及多个级别的块所有权,并且 Hexrays 会产生如下错误:

143019029: control flows out of bounds to 14301902A
143019007: control flows out of bounds to 143018FB1
143019023: control flows out of bounds to 143018FCD

我无法删除、复制或检测这些OFF64 SEGDEF的存在,尽管条件跳转设置了标志位 FF_0OFF。

这些OFF64 SEGDEFs 出现在以前的任何地方abs mov(它们在去混淆过程中被替换),这个问题中的那个以前是:

.text:0000000143EFE8CB 48 BA 5B C3 CF 40 01+  mov     rdx, offset loc_140CFC35B
.text:0000000143EFE8CB 00 00 00

将相同的语句组合到另一个位置不会导致出现。

我试过ida_bytes.del_itemsida_bytes.del_value(隐藏它,但重新填充时它会重新出现), ida_bytes.del_mapping以及其他一些我不记得的东西。nop尽管需要一个 IDAPython 解决方案,但我也尝试过强制重新分析传递的字节

我也尝试了一些与段相关的功能来创造类似的效果,希望我知道如何制作的任何东西都可以删除,但我也失败了;我在基于段的组装方面的经验很少,而且是从 90 年代开始的。

2个回答

尽管 Igor 亲切地回答了我的直接问题,但我仍然面临如何方便地处理这些未使用的fixups 的问题。好在我已经用我自己MakeUnknownPatchBytes功能,所以我可以简单地包括去除fixup那些,忘了整个问题的信息。

我包含此代码作为如何检测 afixup是否存在的示例,因为ida_fixups.exists_fixup如果 afixup隐藏在指令中的意外字节下,则不一定返回您认为的内容

我还包括了一个fixup visitor.

def MyMakeUnknown(ea, nbytes, flags = 0):
    r"""
    @param ea:      any address within the first item to delete (C++: ea_t)
    @param nbytes:  number of bytes in the range to be undefined (C++: asize_t)
    @param flags:   combination of:     DELIT_EXPAND    DELIT_DELNAMES
                                        DELIT_NOTRUNC   DELIT_NOUNAME
                                        DELIT_NOCMT     DELIT_KEEPFUNC
    @param may_destroy: optional callback invoked before deleting a head item.
                        if callback returns false then deletion and operation
                        fail. (C++: may_destroy_cb_t *)
    @return: true on sucessful operation, otherwise false

    Convert item (instruction/data) to unexplored bytes. The whole item
    (including the head and tail bytes) will be destroyed. 
    """
    # check if caller has invoked with (start_ea, end_ea)
    if nbytes > ea:
        nbytes = nbytes - ea

    result = idaapi.del_items(ea, flags, nbytes)
    if not result:
        return result
    
    # check for fixups that must be removed 
    # https://reverseengineering.stackexchange.com/questions/27339/

    fx = idaapi.get_next_fixup_ea(ea - 1)
    while fx < ea + nbytes:
        idaapi.del_fixup(fx)
        fx = idaapi.get_next_fixup_ea(fx)

    return result

def MyMakeUnkn(ea, flags = 0):
    return MyMakeUnknown(ea, 1, flags)

def example_fixup_visitor(ea):
    line = idc.generate_disasm_line(ea, 0)
    print("{:16x} {}".format(ea, line))

def visit_fixups(iteratee):
    ea = idaapi.get_first_fixup_ea()
    while ea != idc.BADADDR:
        iteratee(ea)
        ea = idaapi.get_next_fixup_ea(ea)

# This is a bit length, so it's left until the end.
def PatchBytes(ea, patch=None, comment=None):
    """
    @param ea [optional]:           address to patch (or ommit for screen_ea)
    @param patch list|string|bytes: [0x66, 0x90] or "66 90" or b"\x66\x90"
    @param comment [optional]:      comment to place on first patched line

    @returns int containing nbytes patched

    Can be invoked as PatchBytes(ea, "66 90"), PatchBytes("66 90", ea),
    or just PatchBytes("66 90").
    """



    if isinstance(ea, (list, str)):
        ea, patch = patch, ea
    elif ea is None:
        ea = idc.get_screen_ea()
    old_code = idc.is_code(idc.get_full_flags(idc.get_item_head(ea)))
    old_head = idc.get_item_head(ea)


    if isinstance(patch, str):
        def intify(s): return -1 if '?' in s else int(s, 16)
        patch = [hex_byte_as_pattern_int(x) for x in patch.split(' ')]

    length = len(patch)
    # deal with fixups
    fx = idaapi.get_next_fixup_ea(ea - 1)
    while fx < ea + length:
        idaapi.del_fixup(fx)
        fx = idaapi.get_next_fixup_ea(fx)

    if type(getattr(__builtins__, 'bytes', None)) == 'type' and isinstance(patch, bytes):
        idaapi.patch_bytes(ea, patch)
    else:
        [idc.patch_byte(ea+i, patch[i]) for i in range(length) if patch[i] != -1]
        #  for i in range(length):
            #  if patch[i] != -1:
                #  idc.patch_byte(ea+i, patch[i])

    idc.auto_wait()

    if comment:
        comment_formatted = "[PatchBytes:{:x}-{:x}] {}".format(ea, ea + length, str(comment))
        if 'Commenter' in globals():
            Commenter(old_head, 'line').add(comment_formatted)
        else:
            idaapi.set_cmt(old_head, comment_formatted, 0)

    if old_code:
        head = old_head
        while head < ea + length:
            inslen = idc.get_item_size(head)                                  \
                    if idc.is_code(idc.get_full_flags(idc.get_item_head(ea))) \
                    else idc.create_insn(head)
            if inslen < 1:
                break
            head += inslen

    return length

注释表明地址附加了修正(又名重定位)信息。IDA 在显示数据或反汇编项时试图将其考虑在内,从而导致表达笨拙。如果您确定重定位是伪造的并且经过去混淆后不再使用,您可以使用idaapi.del_fixup(<address>)IDAPython函数将其删除