Is there any way to list all swizzled methods in an iOS app?

后端 未结 1 823
遇见更好的自我
遇见更好的自我 2020-12-16 00:39

I\'m essentially looking for a way to detect when/what third party libraries swizzle. I recently ran into a situation where an ad library used an oddball fork of AFNetworki

相关标签:
1条回答
  • 2020-12-16 01:24

    Here's the closest I was able to get, with a few hours of tinkering. It involves using a fork of mach_override, a couple of DYLD quirks regarding load order, and a stomach for crazy hacks.

    It will only work on the simulator, but that should suffice for the use case you seem to have (I certainly hope you don't have device-only dependencies).

    The meat of the code looks something like this:

    #include <objc/runtime.h>
    #include <mach_override/mach_override.h>
    
    // It is extremely important that we have DYLD run this constructor as soon as the binary loads. If we were to hook
    // this at any other point (for example, another category on NSObject, in the main application), what could potentially
    // happen is other `+load` implementations get invoked before we have a chance to hook `method_exchangeImplementation`,
    // and we don't get to see those swizzles.
    // It is also extremely important that this exists inside its own dylib, which will be loaded by DYLD before _main() is
    // initialized. You must also make sure that this gets loaded BEFORE any other userland dylibs, which can be enforced by
    // looking at the order of the 'link binary with libraries' phase.
    __attribute__((constructor))
    static void _hook_objc_runtime() {
      kern_return_t err;
      MACH_OVERRIDE(void, method_exchangeImplementations, (Method m1, Method m2), &err) {
        printf("Exchanging implementations for method %s and %s.\n", sel_getName(method_getName(m1)), sel_getName(method_getName(m2)));
    
        method_exchangeImplementations_reenter(m1, m2);
      }
      END_MACH_OVERRIDE(method_exchangeImplementations);
    
      MACH_OVERRIDE(void, method_setImplementation, (Method method, IMP imp), &err) {
        printf("Setting new implementation for method %s.\n", sel_getName(method_getName(method)));
    
        method_setImplementation_reenter(method, imp);
      }
      END_MACH_OVERRIDE(method_setImplementation);
    }
    

    Which is surprisingly simple, and produces output like this:

    Exchanging implementations for method description and custom_description.

    There is no good way (without using a breakpoint and looking through the stack trace) to figure out which class is being swizzled, but for most things, just taking a peek at the selectors should give you a hint about where to go from there.

    It appears to work with a couple of categories that I've created that swizzle during +load, and from my reading of mach_override and DYLD's internals, as long as you have your library load order properly setup, you can expect this to be initialized before any other user-land code, if you put it in it's own dynamic library.

    Now, I can't vouch for safety of this, but it seems useful to keep around as a debugging tool, so I've published my example to github:

    https://github.com/richardjrossiii/mach_override_example

    It's MIT licensed, so feel free to use as you see fit.

    0 讨论(0)
提交回复
热议问题