2016-07-20 13 views
8

Ich bin im Wesentlichen auf der Suche nach einer Möglichkeit zu erkennen, wann/welche Bibliotheken von Drittanbietern swizzle. Ich bin kürzlich auf eine Situation gestoßen, in der eine Anzeigenbibliothek eine Sonderform von AFNetworking verwendet hat. AFNetworking swizzles NSURLSessionTask, und die beiden Swizzles spielten unter bestimmten Umständen nicht gut. Ich würde wirklich gerne in der Lage sein, solche Dinge zu erkennen und vernünftig zu kontrollieren, und im Idealfall sogar einen versionierten Dump von jeder gedrehten Methode in der App zu haben, damit wir einen Einblick bekommen, wer und was die Gefahren sind. Die Google- und die Stack-Overflow-Suche haben nur eine Auswahl an Tutorials zum Thema Swizzle ergeben. Ist jemand auf dieses Problem gestoßen oder hat er eine Lösung? Es sieht so aus, als könnte ich etwas mit objc/runtime.h programmieren, aber ich kann mir nicht vorstellen, dass ich die erste Person bin, die das braucht.Gibt es eine Möglichkeit, alle Swizzled-Methoden in einer iOS-App aufzulisten?

+0

Sie könnten versuchen, etwas wie [mach_override] (https://github.com/rentzsch/mach_override) im iOS-Simulator zu verwenden (funktioniert nicht auf dem Gerät) und hooken 'method_exchangeImplementation' /' method_setImplementation', Sie müssen jedoch sicherstellen, dass Sie diesen Codepfad nicht während der Erstellung von Releases verwenden. –

Antwort

8

Hier ist die nächste, die ich bekommen konnte, mit ein paar Stunden Basteln. Es beinhaltet eine Gabel von mach_override, ein paar DYLD-Macken in Bezug auf Ladereihenfolge und einen Bauch für verrückte Hacks.

Es funktioniert nur am Simulator, aber das sollte für den Anwendungsfall ausreichen, den Sie zu haben scheinen (ich hoffe natürlich, dass Sie keine Abhängigkeiten nur für Geräte haben).

Das Fleisch der Code wie folgt aussieht etwas:

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

die überraschend einfach ist, und erzeugt eine Ausgabe wie folgt aus:

Austausch von Implementierungen für Methodenbeschreibung und custom_description.

Es gibt keine gute Möglichkeit (ohne einen Haltepunkt zu verwenden und auf der Suche durch die Stack-Trace), um herauszufinden, welche Klasse umgestellt wird, aber für die meisten Dinge, nur sollten Sie einen Blick auf die Wähler unter geben einen Hinweis auf Wohin man von dort kommt.

Es scheint mit ein paar Kategorien zu arbeiten, die ich, dass swizzle während +load erstellt haben, und aus meiner Lektüre von mach_override und dyld Interna, solange Sie Ihre Bibliothek laden, um richtig eingerichtet haben, können Sie dies erwarten vor jedem anderen Benutzerland-Code initialisiert werden, wenn Sie ihn in seine eigene dynamische Bibliothek einfügen.

Nun, ich kann nicht für die Sicherheit dieser bürgen, aber es scheint nützlich als Debugging-Tool herum zu halten, also habe ich mein Beispiel veröffentlicht GitHub:

https://github.com/richardjrossiii/mach_override_example

Es MIT lizenziert , so fühlen Sie sich frei zu verwenden, wie Sie für richtig halten.

+2

Nice Hack; Wenn man sehr motiviert war, konnte man das neue IMP zwischenspeichern und dann alle Klassen durchsuchen, um herauszufinden, wo es injiziert wurde. Es wäre Brute-Force-Verhalten und könnte zu unerwartetem Verhalten führen (weil Sie am Ende die Initialisierung von ever-Klasse in der Laufzeit auslösen, wenn Sie nicht wirklich vorsichtig sind). – bbum

+0

@bbum Das ist sicherlich ein Ansatz, obwohl die meisten Swizzling direkt von '+ load' ausgeht (Sperren von dynamischen Swizzling-Bibliotheken wie OCMock und ähnlichem), wäre es wahrscheinlich einfacher' backtrace() 'kombiniert mit' dladdr() zu verwenden. 'um einfach die Klasse & Kategorie zu holen, die 'method_exchangeImplementation' aufgerufen hat :) –

+1

Das funktioniert manchmal, wenn du wissen willst, woher der Swizzle kommt, aber nicht, dass die Klasse getauscht wird?Wenn ich Swizzling verwendet habe, tausche ich eine ganze Reihe von Methoden über mehrere Klassen hinweg zu Debug-Zwecken. ? – bbum