2016-07-20 6 views
8

Я по существу ищу способ обнаружить, когда/то, что происходит в сторонних библиотеках. Недавно я столкнулся с ситуацией, когда в рекламной библиотеке использовалась нечетная вилка AFNetworking. AFNetworking swizzles NSURLSessionTask, и два swizzles не играли хорошо при определенных обстоятельствах. Я бы очень хотел, чтобы я смог обнаружить и проверить, что это такое, и в идеале даже держите в приложении все варианты, которые можно использовать в любом случае, поэтому у нас есть определенная информация о том, кто обезьяна исправляет то, что и какие риски. Google и поиск переполнения стека оказались ничем иным, как кучей учебника о том, как подпрыгивать. Кто-нибудь сталкивается с этой проблемой или имеет решение? Похоже, что я мог бы что-то кодировать с помощью objc/runtime.h, но я не могу себе представить, что я первый человек, которому это нужно.Есть ли способ перечислить все swizzled методы в приложении iOS?

+0

Вы можете попытаться использовать что-то вроде [mach_override] (https://github.com/rentzsch/mach_override) в iOS-симуляторе (не будет работать на устройстве) и перехватить 'method_exchangeImplementation' /' method_setImplementation', хотя вам нужно будет убедиться, что вы не используете этот кодовый путь во время сборки релиза. –

ответ

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.

Там нет хорошего способа (без использования точки останова и просматривая трассировки стеки), чтобы выяснить, какой класс идет swizzled, но для большинства вещей, просто принимая взглянуть на селекторах должны дать вам подсказку о том, куда идти оттуда.

Оказывается, работать с несколькими категориями, которые я создал, что напиваться во время +load, и от моего чтения mach_override и внутренних DYLD, до тех пор пока у вас есть ваша библиотека порядок загрузки правильно настройки, вы можете ожидать, что это инициализироваться перед любым другим кодом пользователя-земли, если вы поместите его в свою собственную динамическую библиотеку.

Теперь я не могу ручаться за безопасность, но это, кажется, полезно держать вокруг как инструмент отладки, поэтому я опубликовал свой пример GitHub:

https://github.com/richardjrossiii/mach_override_example

Это MIT лицензированы , поэтому не стесняйтесь использовать, как вы сочтете нужным.

+2

Nice hack; если бы кто-то был ужасно мотивирован, можно было кэшировать новый IMP, а затем искать все классы, чтобы найти, где он был введен. Это было бы грубой силой и потенциально вызвало бы неожиданное поведение (потому что вы в конечном итоге инициируете + инициализацию любого класса во время выполнения, если не будете очень осторожны). – bbum

+0

@bbum Это, безусловно, один подход, хотя рассмотрение большинства swizzling происходит из непосредственно внутри '+ load' (запрет динамических swizzling-библиотек, таких как OCMock и т. Д.), Вероятно, было бы проще просто использовать' backtrace() 'в сочетании с' dladdr() 'просто захватить класс и категорию, которые вызывают' method_exchangeImplementation' :) –

+1

Это будет иногда работать, если вы хотите узнать, откуда появился swizzle, но не классный swizzled?Когда я использовал swizzling, я часто набрасываю кучу методов на кучу классов для целей отладки. ? – bbum

 Смежные вопросы

  • Нет связанных вопросов^_^