1

This has plagued me from IDA 6.5 to 7.5.

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

It may not look like much, but when a conditional jump is placed over such a location, the result is debilitating.

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

Every instance creates an xref to the third .text segment at 0x143019000 along with multiple levels of chunk ownership, and Hexrays produces errors such as:

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

I cannot remove, reproduce or detect the presence of these OFF64 SEGDEF lines, though the conditional jumps have the flag bit FF_0OFF set.

These OFF64 SEGDEFs appear where-ever an abs mov was previously (they're being replaced in a de-obfuscation process), the one in this question previously being:

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

Assembling an identical statement to another another location does not cause one to appear.

I have tried ida_bytes.del_items, ida_bytes.del_value (hides it, but it re-appears when repopulated), ida_bytes.del_mapping and some other things I don't recall. I've also tried forcing re-analysis with the bytes nopped over, though a need an IDAPython solution.

I've also tried some segment related functions in an effort to create a similar effect, in the hope the anything I know how to make I can then remove, but I failed in this effort also; my experience in segment-based assembly is minimal and from the 90's.

0

The comment indicates that there is fixup (aka relocation) information attached to the address. IDA tries to take it into account when displaying data or disassembly items, resulting in awkward expressions. If you're sure that the relocation is bogus and is no longer used after deobfuscation, you can delete it using idaapi.del_fixup(<address>) IDAPython function.

1

Though Igor kindly answered my immediate question, I was still left with the problem of how to conveniently dispose of these unused fixups. Fortunately I already use my own MakeUnknown and PatchBytes function, so I was able to simply include removal of fixup information with those and forget about the entire problem.

I am including this code as an example of how to detect if a fixup is present, as ida_fixups.exists_fixup doesn't necessarily return what you think it will, if a fixup is hiding under an unexpected byte within an instruction.

I've also included a 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

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.