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?
Odpowiedz
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.
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
@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' :) –
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
- 1. Czy istnieje sposób na pobranie identyfikatora grupy aplikacji z aplikacji hosta i udostępnienie rozszerzenia na iOS?
- 2. Czy istnieje sposób na zlokalizowanie aplikacji na różnych platformach?
- 3. Czy istnieje sposób na uzyskanie wszystkich zarządzanych jednostek z EntityManager
- 4. Czy istnieje sposób na odzyskanie wszystkich rekordów w kwerendzie (ElasticSearch)?
- 5. Czy istnieje sposób na usunięcie wszystkich lepkich opcji w CVS?
- 6. Rejestrowanie wszystkich wywołań metod w aplikacji Railsy
- 7. Testowanie na iOS: czy istnieje sposób na pominięcie testów?
- 8. Czy istnieje sposób na uzyskanie wszystkich wartości w NSUserDefaults?
- 9. Czy istnieje sposób na importowanie wszystkich plików częściowych w SASS?
- 10. Czy istnieje sposób na wylistowanie wszystkich dostępnych tras w aplikacji Nancy?
- 11. Czy istnieje sposób uruchamiania polecenia na wszystkich dynamach Heroku?
- 12. Czy istnieje sposób, aby programowo wyświetlić listę wszystkich zależności gradle?
- 13. Czy istnieje sposób na zawijanie wszystkich metod JavaScript za pomocą funkcji?
- 14. Czy istnieje sposób na listę wszystkich dostępnych liter dysków w pythonie?
- 15. Wyświetl listę wszystkich zainstalowanych aplikacji.
- 16. Czy istnieje sposób na zresetowanie wszystkich właściwości statycznych określonej klasy?
- 17. Czy istnieje sposób na przebudzenie aplikacji na iOS po otrzymaniu powiadomienia wypychania, nawet gdy zostanie zabity?
- 18. Czy istnieje sposób na powtórzenie wszystkich wartości wyliczeniowych?
- 19. Czy istnieje sposób na listę wersji java wszystkich moich zależności maven?
- 20. Czy istnieje sposób na usunięcie wszystkich połączeń klientów Redis?
- 21. Android AlarmManager: czy istnieje sposób na anulowanie WSZYSTKICH ustawionych alarmów?
- 22. Czy można wygenerować listę podłączonych urządzeń bluetooth na iOS?
- 23. Czy istnieje sposób na wyłączenie panelu środowiska w aplikacji RStudio?
- 24. Czy istnieje sposób na przejrzenie aplikacji Rails w debugerze?
- 25. Lista wszystkich aplikacji zainstalowanych na urządzeniu z systemem iOS i ich pozycja na ekranie głównym (Springboard)
- 26. Najlepszy sposób na dystrybucję mojej aplikacji na iOS do testerów?
- 27. Czy istnieje sposób na uzyskanie wszystkich plików z blob z błękitnym
- 28. Wygeneruj listę metod klasy z typami metod
- 29. (iOS) Czy istnieje sposób wycofania mojego zgłoszenia aplikacji?
- 30. iOS: Czy istnieje sposób na sfałszowanie ustawień regionalnych użytkownika?
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. –