12

我本质上是在寻找一种方法来检测第三方库何时/什么发生混乱。我最近遇到了一个广告库使用 AFNetworking 的古怪分支的情况。AFNetworking swizzles NSURLSessionTask,这两个 swizzles 在某些情况下并没有很好地发挥作用。我真的很想能够检测和健全检查这种事情,理想情况下,甚至可以在应用程序中保留每个 swizzled 方法的版本转储,这样我们就可以了解谁在修补什么以及风险是什么。谷歌和堆栈溢出搜索除了一堆关于如何调配的教程之外什么都没有出现。有人遇到这个问题或有解决方案吗?看起来我可以使用 objc/runtime.h 编写一些东西,但我无法想象我是第一个需要这个的人。

4

1 回答 1

9

这是我能得到的最接近的,经过几个小时的修补。它涉及使用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);
}

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

交换方法描述和 custom_description 的实现。

没有很好的方法(不使用断点并查看堆栈跟踪)来确定哪个类正在被混合,但是对于大多数事情,只需查看选择器就应该给你一个关于从那里去哪里的提示。

它似乎可以与我在 期间创建的几个类别一起使用+load,并且根据我对mach_overrideDYLD 内部的阅读,只要您正确设置了库加载顺序,您就可以期望在任何其他用户之前对其进行初始化-land 代码,如果你把它放在它自己的动态库中。

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

https://github.com/richardjrossiii/mach_override_example

它是 MIT 许可的,因此请随意使用。

于 2016-07-20T21:16:25.490 回答