我基本上正在寻找一种方法来检测第三方图书馆何时/哪些人调整。我最近遇到了一个广告库使用AFNetworking的古怪分叉的情况。 AFNetworking对NSURLSessionTask进行了调整,并且在某些情况下这两个混合体并没有很好地发挥作用。我真的希望能够检测并理智检查这种事情,理想情况下甚至可以在应用程序中保留每个混合方法的版本转储,这样我们就可以了解谁的猴子修补什么和什么风险是。谷歌和堆栈溢出搜索只发布了一堆关于如何调整的教程。有人遇到过这个问题或有解决方案吗?看起来我可以使用objc / runtime.h编写代码,但我无法想象我是第一个需要它的人。
答案 0 :(得分:8)
这是我能够得到的最接近的,经过几个小时的修补。它涉及使用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_override
和DYLD内部的阅读,只要您正确地加载了库加载顺序设置,如果你把它放在它自己的动态库中,你可以在任何其他用户域代码之前初始化它。
现在,我无法保证这一点的安全性,但作为一个调试工具保持不变似乎很有用,所以我将我的例子发布到github:
https://github.com/richardjrossiii/mach_override_example
这是麻省理工学院的许可,所以请随意使用。