Objective-C
(Probably only if compiled with clang on 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
This code uses several tricks to get to the unused function. First is the value 0x2A27. This is a tagged pointer for the integer 42, which encodes the value in the pointer to avoid allocating an object.
Next is MyClass
. It is never used, but the runtime calls the +load
method when it is loaded, before main
. This dynamically creates and registers a new class, using NSValue
as its superclass. It also adds a +load
method for that class, using MyClass
's -unusedMethod
as the implementation. After registration, it calls the load method on the new class (for some reason it isn't called automatically).
Since the new class's load method uses the same implementation as unusedMethod
, that is effectively called. It takes the superclass of itself, and adds unusedFunction
as an implementation for that class's doesNotRecognizeSelector:
method. This method was originally an instance method on MyClass
, but is being called as a class method on the new class, so self
is the new class object. Therefore, the superclass is NSValue
, which is also the superclass for NSNumber
.
Finally, main
runs. It takes the pointer value and sticks it in a NSString *
variable (the __bridge
and first cast to void *
allow this to be used with or without ARC). Then, it tries to call stringByAppendingString:
on that variable. Since it is actually a number, which does not implement that method, the doesNotRecognizeSelector:
method is called instead, which travels up through the class hierarchy to NSValue
where it is implemented using unusedFunction
.
Note: the incompatibility with other systems is due to the tagged pointer usage, which I do not believe has been implemented by other implementations. If this were replaced with a normally created number the rest of the code should work fine.