-
Notifications
You must be signed in to change notification settings - Fork 53
Examples | Finding all call instructions that reference a function table by register
Ali Rizvi-Santiago edited this page Mar 10, 2023
·
8 revisions
table = db.address('gpf_libIdaTable_3656c0')
for ea in db.up(table):
if not database.type.code(ea): print(db.disasm(ea), 'is not referencing an instruction')
elif ins.mnemonic(ea) != 'lea': print(db.disasm(ea), 'is unexpected')
table = db.address('gpf', 'libIdaTable', 0x3656c0)
for ea in db.x.up(table):
# continue searching while we're not at a CF_STOP instruction... like an unconditional branch, a return, hlt, etc.
while_not_sentinel = lambda ea: not ins.type.sentinel(ea)
# grab all the operands that are being written to, and then filter them for registers
ops_written_to = ins.ops_write(ea)
regs_written_to = [op for op in ops_written_to if isinstance(op, register_t)]
# find the next instruction that reads or executes something using our register
target = db.nextreg(ea, while_not_sentinel, *regs_written_to, read=True, execute=True)
# tag it with the register name and start address so that if we need to correct, we have a place to start.
reg, = regs_written_to
tag = reg.name, ea
db.tag(target, 'libIdaTable.call', tag)
tag = 'libIdaTable.call'
results = []
# we really should be using list comprehensions by now
for f, _ in db.selectcontents(tag):
for ea, res in func.select(f, tag):
# if it's not a call, then unpack the register and our start address to save to a list
if not ins.type.call(ea):
reg, start = res[tag]
results.append((ea, reg))
continue
continue
# print them out, and then filter our list for any instructions that don't use our register whatsoever
print('\n'.join(map(db.disasm, map(first, results))))
results = [(ea, reg) for ea, reg in results if not ins.ops_register(ea, reg)]
for ea, reg in results:
while_not_sentinel = lambda ea: not ins.type.sentinel(ea)
# follow the branch that we landed on and grab what it points to
branch_tgt = ins.op(ea, 0)
# find the next time the reg is either read from or executed
target = db.nextreg(branch_tgt, while_not_sentinel, reg, read=True, execute=True)
# now we can move our tag from the old location to the new one.
oldtag = db.tag(ea, 'libIdaTable.call', None)
db.tag(target, 'libIdaTable.call', oldtag)
def selectall(*tags):
'''because some people can't read list comprehensions'''
for f, _ in db.selectcontents(*tags):
for ea, res in func.select(f, *tags):
yield ea, res
continue
return
# find all our results again
missed, not_branch = [], []
for ea, res in selectall('libIdaTable.call'):
reg, start = res['libIdaTable.call']
# gather instructions that don't actually use our register
if not ins.ops_register(ea, reg):
missed.append(ea)
# gather instructions that aren't referencing a branch or call (might be loaded into another reg)
if not ins.type.call(ea) and not ins.type.branch(ea):
not_branch.append(ea)
continue
# output the things that we missed
for ea in missed:
reg, start = db.tag(ea, 'libIdaTable.call')
print(db.disasm(start), reg, db.disasm(ea))
# maybe we can bruteforce finding the register being used to execute something
for opref in func.register(start, reg, execute=True):
print(hex(start), reg, db.disasm(opref))
continue
# find all non-branches that are being stored to a non-register (probably a local)
for ea in not_branch:
if all(not isinstance(op, register_t) for op in ins.ops_write(ea)):
print(db.disasm(ea))
continue
import hexrays
table = db.address('gpf_libIdaTable_3656c0')
symbolname = db.name(table)
# collect all ctree matches that look like "(symbol[index])(...)" and only care about their address
candidates = {}
for f in not_branch:
candidates[f] = set()
for item in hexrays.match(f, '({:s}[_])(_);'.format(symbolname)):
ea = hexrays.address(item)
candidates[f].add(ea)
continue
# we could check them with something like
everything = [ [(ea, item) for item in items] for ea, items in candidates.items() ]
print('\n'.join("{:s}\t\t{:s}".format(db.disasm(ea, comments=True), db.disasm(item)) for ea, item in itertools.chain(*everything)))
# but we don't care anymore, and so we move the tag blindly
for ea, items in candidates.items():
tag = db.tag(ea, 'libIdaTable.call', None)
for call in items:
db.tag(ea, 'libIdaTable.call', tag)
continue
|