Ціль-С
(Мабуть, лише якщо їх компілювали з clang на 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
Цей код використовує кілька хитрощів, щоб дістатися до невикористаної функції. По-перше, це значення 0x2A27. Це помічений покажчик для цілого числа 42, який кодує значення вказівника, щоб уникнути виділення об'єкта.
Далі є MyClass
. Він ніколи не використовується, але час виконання викликає +load
метод під час його завантаження main
. Це динамічно створює та реєструє новий клас, використовуючи NSValue
як його суперклас. Він також додає +load
метод для цього класу, використовуючи MyClass
's -unusedMethod
як реалізацію. Після реєстрації він викликає метод завантаження нового класу (чомусь його не викликають автоматично).
Оскільки метод завантаження нового класу використовує таку ж реалізацію unusedMethod
, що і ефективно називається. Він приймає суперклас сам по собі і додає unusedFunction
як реалізацію doesNotRecognizeSelector:
методу цього класу . Спочатку цей метод був методом екземпляра MyClass
, але називається методом класу для нового класу, self
як і об'єкт нового класу. Тому суперклас є NSValue
, що також є суперкласом NSNumber
.
Нарешті, main
бігає. Він приймає значення вказівника і вставляє його в NSString *
змінну ( __bridge
і перший склад, щоб void *
дозволити використовувати це з ARC або без нього). Потім він намагається викликати stringByAppendingString:
цю змінну. Оскільки це насправді число, яке не реалізує цей метод, doesNotRecognizeSelector:
замість цього називається метод, який переміщається через ієрархію класів до місця, NSValue
де він реалізований за допомогою unusedFunction
.
Примітка: несумісність з іншими системами пояснюється використанням тегів, які, на мою думку, не були реалізовані іншими реалізаціями. Якщо це було замінено на звичайно створене число, решта коду повинна спрацьовувати нормально.