C objetivo
(Probablemente solo si se compila con clang en 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 varios trucos para acceder a la función no utilizada. Primero es el valor 0x2A27. Este es un puntero etiquetado para el entero 42, que codifica el valor en el puntero para evitar la asignación de un objeto.
Siguiente es MyClass
. Nunca se usa, pero el tiempo de ejecución llama al +load
método cuando se carga, antes main
. Esto crea y registra dinámicamente una nueva clase, utilizando NSValue
como su superclase. También agrega un +load
método para esa clase, utilizando MyClass
's -unusedMethod
como implementación. Después del registro, llama al método de carga en la nueva clase (por alguna razón no se llama automáticamente).
Dado que el método de carga de la nueva clase usa la misma implementación que unusedMethod
, eso se llama efectivamente. Toma la superclase de sí mismo y agrega unusedFunction
como implementación para el doesNotRecognizeSelector:
método de esa clase . Este método era originalmente un método de instancia MyClass
, pero se llama como un método de clase en la nueva clase, también lo self
es el nuevo objeto de clase. Por lo tanto, la superclase es NSValue
, que también es la superclase para NSNumber
.
Finalmente, main
corre. Toma el valor del puntero y lo pega en una NSString *
variable (la __bridge
primera conversión para void *
permitir que se use con o sin ARC). Luego, trata de invocar stringByAppendingString:
esa variable. Dado que en realidad es un número, que no implementa ese método, doesNotRecognizeSelector:
se llama al método en su lugar, que viaja a través de la jerarquía de clases hasta NSValue
donde se implementa utilizando unusedFunction
.
Nota: la incompatibilidad con otros sistemas se debe al uso del puntero etiquetado, que no creo que haya sido implementado por otras implementaciones. Si esto fuera reemplazado por un número creado normalmente, el resto del código debería funcionar bien.