Though Igor kindly answered my immediate question, I was still left with the problem of how to conveniently dispose of these unused fixup
s. 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