Objetivo-C
(Provavelmente apenas se compilado com clang no 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
Este código usa vários truques para acessar a função não utilizada. Primeiro é o valor 0x2A27. Este é um ponteiro marcado para o número inteiro 42, que codifica o valor no ponteiro para evitar a alocação de um objeto.
O próximo é MyClass. Ele nunca é usado, mas o tempo de execução chama o +loadmétodo quando ele é carregado antes main. Isso cria e registra dinamicamente uma nova classe, usando NSValuecomo superclasse. Ele também adiciona um +loadmétodo para essa classe, usando MyClass's -unusedMethodcomo implementação. Após o registro, ele chama o método load na nova classe (por algum motivo, não é chamado automaticamente).
Como o método de carregamento da nova classe usa a mesma implementação unusedMethodque é efetivamente chamada. Ele pega a superclasse e adiciona unusedFunctioncomo uma implementação para o doesNotRecognizeSelector:método dessa classe . Esse método era originalmente um método de instância MyClass, mas está sendo chamado como método de classe na nova classe, assim selfcomo o novo objeto de classe. Portanto, a superclasse é NSValue, que também é a superclasse NSNumber.
Finalmente, maincorre. Ele pega o valor do ponteiro e o cola em uma NSString *variável (a __bridgeprimeira conversão para void *permitir que isso seja usado com ou sem ARC). Em seguida, ele tenta chamar stringByAppendingString:essa variável. Como na verdade é um número, que não implementa esse método, o doesNotRecognizeSelector:método é chamado, que viaja pela hierarquia de classes até NSValueonde é implementado usando unusedFunction.
Nota: a incompatibilidade com outros sistemas se deve ao uso do ponteiro marcado, que não acredito que tenha sido implementado por outras implementações. Se isso foi substituído por um número criado normalmente, o restante do código deve funcionar bem.