Objective-C
(Probabilmente solo se compilato con clang su Mac OS X)
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
void unusedFunction(void) {
printf("huh?\n");
exit(0);
}
int main() {
NSString *string;
string = (__bridge id)(void*)0x2A27; // Is this really valid?
NSLog(@"%@", [string stringByAppendingString:@"foo"]);
return 0;
}
@interface MyClass : NSObject
@end
@implementation MyClass
+ (void)load {
Class newClass = objc_allocateClassPair([NSValue class], "MyClass2", 0);
IMP imp = class_getMethodImplementation(self, @selector(unusedMethod));
class_addMethod(object_getClass(newClass), _cmd, imp, "");
objc_registerClassPair(newClass);
[newClass load];
}
- (void)unusedMethod {
Class class = [self superclass];
IMP imp = (IMP)unusedFunction;
class_addMethod(class, @selector(doesNotRecognizeSelector:), imp, "");
}
@end
Questo codice utilizza diversi trucchi per accedere alla funzione non utilizzata. Il primo è il valore 0x2A27. Questo è un puntatore con tag per l'intero 42, che codifica il valore nel puntatore per evitare di allocare un oggetto.
Il prossimo è MyClass
. Non viene mai utilizzato, ma il runtime chiama il +load
metodo quando viene caricato, prima main
. Questo crea e registra dinamicamente una nuova classe, usando NSValue
come superclasse. Aggiunge anche un +load
metodo per quella classe, usando MyClass
's -unusedMethod
come implementazione. Dopo la registrazione, chiama il metodo di caricamento sulla nuova classe (per qualche motivo non viene chiamato automaticamente).
Poiché il metodo di caricamento della nuova classe utilizza la stessa implementazione di unusedMethod
, viene effettivamente chiamato. Prende la superclasse di se stesso e aggiunge unusedFunction
come implementazione il doesNotRecognizeSelector:
metodo di quella classe . Questo metodo era originariamente un metodo di istanza attivo MyClass
, ma viene chiamato come metodo di classe nella nuova classe, così self
come il nuovo oggetto classe. Pertanto, la superclasse è NSValue
, che è anche la superclasse per NSNumber
.
Finalmente main
corre. Prende il valore del puntatore e lo inserisce in una NSString *
variabile (il __bridge
primo e il cast per void *
consentirne l'utilizzo con o senza ARC). Quindi, tenta di richiamare stringByAppendingString:
quella variabile. Poiché in realtà è un numero, che non implementa quel metodo, doesNotRecognizeSelector:
viene invece chiamato il metodo, che viaggia attraverso la gerarchia di classi fino a NSValue
dove viene implementato usando unusedFunction
.
Nota: l'incompatibilità con altri sistemi è dovuta all'utilizzo del puntatore con tag, che non credo sia stato implementato da altre implementazioni. Se questo è stato sostituito con un numero normalmente creato, il resto del codice dovrebbe funzionare correttamente.