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 +load
método quando ele é carregado antes main
. Isso cria e registra dinamicamente uma nova classe, usando NSValue
como superclasse. Ele também adiciona um +load
método para essa classe, usando MyClass
's -unusedMethod
como 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 unusedMethod
que é efetivamente chamada. Ele pega a superclasse e adiciona unusedFunction
como 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 self
como o novo objeto de classe. Portanto, a superclasse é NSValue
, que também é a superclasse NSNumber
.
Finalmente, main
corre. Ele pega o valor do ponteiro e o cola em uma NSString *
variável (a __bridge
primeira 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é NSValue
onde é 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.