Mục tiêu-C
(Có lẽ chỉ khi được biên dịch bằng tiếng kêu trên 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
Mã này sử dụng một số thủ thuật để có được chức năng không sử dụng. Đầu tiên là giá trị 0x2A27. Đây là một con trỏ được gắn thẻ cho số nguyên 42, mã hóa giá trị trong con trỏ để tránh phân bổ một đối tượng.
Tiếp theo là MyClass
. Nó không bao giờ được sử dụng, nhưng thời gian chạy gọi +load
phương thức khi nó được tải, trước đó main
. Điều này tự động tạo và đăng ký một lớp mới, sử dụng NSValue
như siêu lớp của nó. Nó cũng cho biết thêm một +load
phương pháp cho lớp đó, sử dụng MyClass
's-unusedMethod
như việc thực hiện. Sau khi đăng ký, nó gọi phương thức tải trên lớp mới (vì một số lý do, nó không được gọi tự động).
Vì phương thức tải của lớp mới sử dụng cùng cách thực hiện unusedMethod
, nên được gọi một cách hiệu quả. Nó lấy siêu lớp của chính nó và thêm vào unusedFunction
như là một triển khai cho doesNotRecognizeSelector:
phương thức của lớp đó . Phương thức này ban đầu là một phương thức cá thể trên MyClass
, nhưng được gọi là phương thức lớp trên lớp mới, self
đối tượng lớp mới cũng vậy. Do đó, siêu lớp là NSValue
, cũng là siêu lớp cho NSNumber
.
Cuối cùng, main
chạy. Nó lấy giá trị con trỏ và dán nó vào một NSString *
biến ( __bridge
lần đầu tiên và lần đầu tiên void *
cho phép điều này được sử dụng có hoặc không có ARC). Sau đó, nó cố gắng gọi stringByAppendingString:
vào biến đó. Vì nó thực sự là một số, không thực hiện phương thức đó, nên doesNotRecognizeSelector:
phương thức được gọi thay thế, nó đi lên qua hệ thống phân cấp lớp đến NSValue
nơi nó được thực hiện bằng cách sử dụng unusedFunction
.
Lưu ý: sự không tương thích với các hệ thống khác là do việc sử dụng con trỏ được gắn thẻ, điều mà tôi không tin là đã được triển khai bởi các triển khai khác. Nếu điều này được thay thế bằng một số được tạo bình thường, phần còn lại của mã sẽ hoạt động tốt.