"""Resolve the concrete owner behind request+0x08 for the SBC category request. q_md_sbc_9 proved generic slot +0x88 (0x1801631e0) invokes: owner = *(request + 8) owner.vtable[+0x18](owner, parsed_response, 0) Work backwards from the category request constructor and its callers to identify who supplies request+8, then map candidate owner vtables and their +0x18 consumers. """ import traceback try: def show(a, label): f = func(a) print("\n=== %s %#x %s ===" % (label, a, f.getName() if f else "?")) print(dec(a)) ctor = 0x18017a7c0 show(ctor, "category request constructor") print("\n=== ctor callers ===") for ent, name in callers(ctor): print(" %#x %s" % (ent, name)) show(ent, "ctor caller") print("\n=== ctor xrefs ===") for frm, typ, name, ent in xrefs_to(ctor): print(" from=%#x type=%s fn=%s entry=%#x" % (frm, typ, name, ent)) # The request base constructor is usually visible as the first direct call in # the category constructor. Dump every direct callee so request+8 initialization # can be distinguished from URI/tag setup. print("\n=== constructor direct callees ===") for target, name in callees(ctor): print(" %#x %s" % (target, name)) show(target, "ctor callee") # Ghidra did not create a function at the traced +0x90 thunk. Print its raw # instructions and nearby containing-function identity without assuming a body. print("\n=== raw callback thunk at 0x180154830 ===") ad = addr(0x180154830) for _ in range(48): ins = listing.getInstructionAt(ad) if ins is None: print(" %s " % ad) ad = ad.add(1) continue print(" %s %s" % (ad, ins)) ad = ins.getNext().getAddress() if ins.getNext() else ad.add(ins.getLength()) except Exception: traceback.print_exc()