有什么方法可以列出 iOS 应用程序中的所有混合方法吗?

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

我本质上是在寻找一种方法来检测 when/what 第三方库 swizzle。我最近 运行 遇到一个广告库使用 AFNetworking 的古怪分支的情况。 AFNetworking swizzles NSURLSessionTask,这两个swizzles在某些情况下表现不佳。我真的很想能够检测和健全检查这类事情,理想情况下甚至在应用程序中保留每个混合方法的版本转储,这样我们就可以了解谁在修补什么以及风险是什么。 Google 和堆栈溢出搜索只找到了一堆关于如何调配的教程。有人 运行 解决过这个问题或有解决方案吗?看起来我可以使用 objc/runtime.h 编写一些代码,但我无法想象我是第一个需要它的人。

这是我能够得到的最接近的结果,经过几个小时的修修补补。它涉及使用 mach_override 的分支,一些关于加载顺序的 DYLD 怪癖,以及对疯狂黑客的胃口。

它只能在模拟器上运行,但这应该足以满足您似乎拥有的用例(我当然希望您没有仅设备依赖项)。

代码的主要内容如下所示:

#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);
}

这非常简单,并产生如下输出:

Exchanging implementations for method description and custom_description.

没有很好的方法(不使用断点并查看堆栈跟踪)来找出哪个 class 正在被调配,但对于大多数情况,只需看一下选择器就可以了关于从那里去哪里的提示。

它似乎适用于我在 +load 期间创建的几个类别,从我阅读 mach_override 和 DYLD 的内部结构来看,只要您加载了库为了正确设置,如果你把它放在它自己的动态库中,你可以期望它在任何其他用户代码之前被初始化。

现在,我不能保证它的安全性,但作为调试工具保留它似乎很有用,所以我已将我的示例发布到 github:

https://github.com/richardjrossiii/mach_override_example

它已获得麻省理工学院许可,请随意使用。