2016-07-20 10 views
8

Zasadniczo szukam sposobu, aby wykryć, kiedy/jakie biblioteki stron trzecich swizzle. Niedawno natknąłem się na sytuację, w której biblioteka reklam wykorzystała dziwne widełki AFNetworking. AFNetworking swizz NSURLSessionTask, a te dwa zawroty nie grały ładnie w pewnych okolicznościach. Naprawdę chciałbym móc wykryć i sprawdzić, jak to wygląda, a najlepiej nawet przechowywać wersję wszystkich zawiłych metod w aplikacji, abyśmy mieli pewną widoczność, kto łata małpy, co i jakie jest ryzyko. Wyszukiwarka Google i przepełnienie stosu okazały się tylko zestawem samouczków na temat tego, w jaki sposób zawrotną rozmowę. Ktoś wpada na ten problem lub ma rozwiązanie? Wygląda na to, że mógłbym napisać coś za pomocą objc/runtime.h, ale nie mogę sobie wyobrazić, że jestem pierwszą osobą, która tego potrzebuje.Czy istnieje sposób na listę wszystkich metod z użyciem skryptów w aplikacji na iOS?

+0

Możesz spróbować użyć czegoś takiego jak [mach_override] (https://github.com/rentzsch/mach_override) w symulatorze iOS (nie będzie działać na urządzeniu) i zawiesić 'method_exchangeImplementation' /' method_setImplementation', chociaż musisz upewnić się, że nie używasz tej ścieżki kodu podczas kompilacji wersji. –

Odpowiedz

8

Oto najbliższy, jaki udało mi się uzyskać, po kilku godzinach majsterkowania. Polega na użyciu widelca mach_override, kilku dziwactw DYLD dotyczących kolejności ładunków i żołądka dla szalonych hacków.

Będzie działać tylko na symulatorze, ale to powinno wystarczyć na przypadek użycia, który wydaje się mieć (mam oczywiście nadzieję, że nie masz zależnych od urządzenia zależności).

Mięso kodu wygląda mniej więcej tak:

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

Która jest zaskakująco prosty i produkuje tak:

Wymiana implementacje dla opisu metody i custom_description.

Nie ma dobry sposób (bez użycia przerwania i pominie ślad stosu), aby dowiedzieć się, która klasa jest swizzled, ale dla większości rzeczy, po prostu biorąc okiem na selektorów powinno dać wskazówkę na temat dokąd pójść.

Wydaje się pracować z kilku kategorii, które stworzyłem, że swizzle podczas +load iz mojego czytania mach_override i wewnętrznych dyld jest, tak długo, jak masz biblioteki obciążenie celu dokonania właściwej konfiguracji można spodziewać się tego, aby być zainicjalizowane przed jakimkolwiek innym kodem kraju użytkownika, jeśli umieścisz go w swojej własnej dynamicznej bibliotece.

Teraz nie mogę ręczyć za bezpieczeństwo tego, ale wydaje się użyteczne, aby zachować wokół jako narzędzie do debugowania, więc już opublikowane moim przykładem do GitHub:

https://github.com/richardjrossiii/mach_override_example

To MIT licencjonowany , więc nie krępuj się używać według własnego uznania.

+2

Fajny hack; gdyby ktoś był okropnie zmotywowany, można buforować nowy IMP, a następnie przeszukać wszystkie klasy, aby znaleźć miejsce, w którym został wstrzyknięty. Byłaby to brutalna siła i potencjalnie powodowałaby nieoczekiwane zachowanie (ponieważ skończy się wywoływanie + inicjalizacji kiedykolwiek klasy w środowisku wykonawczym, chyba że jesteś naprawdę ostrożny). – bbum

+0

@bbum To z pewnością jedno podejście, chociaż biorąc pod uwagę, że większość swizzlingów ma miejsce bezpośrednio w '+ load' (z pominięciem dynamicznych bibliotek swizzlingowych, takich jak OCMock i podobne), prawdopodobnie łatwiej byłoby po prostu użyć' backtrace() 'w połączeniu z' dladdr() 'po prostu chwyć klasę i kategorię, która wywołała' method_exchangeImplementation' :) –

+1

To czasami zadziała, jeśli chcesz wiedzieć, skąd się wziął ten swizzle, ale czy ta klasa nie jest już zakręcony?Kiedy używam swizzling, jestem często masowo przekręcający kilka metod w różnych klasach dla celów debugowania. ? – bbum

Powiązane problemy