Objective-C
(Вероятно, только если скомпилировано с 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качестве реализации. После регистрации он вызывает метод load для нового класса (по некоторым причинам он не вызывается автоматически).
Так как метод загрузки нового класса использует ту же реализацию unusedMethod, что и, он эффективно вызывается. Он берет суперкласс сам по себе и добавляет unusedFunctionв качестве реализации doesNotRecognizeSelector:метод этого класса . Этот метод изначально был методом экземпляра MyClass, но вызывается как метод класса для нового класса, так же selfкак и новый объект класса. Следовательно, суперкласс есть NSValue, который также является суперклассом для NSNumber.
Наконец-то mainработает. Он принимает значение указателя и помещает его в NSString *переменную ( __bridgeи первое приведение, чтобы void *позволить использовать его с ARC или без него). Затем он пытается вызвать stringByAppendingString:эту переменную. Поскольку на самом деле это число, которое не реализует этот метод, doesNotRecognizeSelector:вместо этого вызывается метод, который перемещается вверх по иерархии классов туда, NSValueгде он реализован с использованием unusedFunction.
Примечание. Несовместимость с другими системами связана с использованием тегового указателя, который, как я полагаю, не был реализован другими реализациями. Если это было заменено на обычно создаваемый номер, остальная часть кода должна работать нормально.