前段时间,同事给看了一段有趣的代码。对于这段代码执行的结果的出乎意外,我们产生了各种的猜测。但猜测毕竟只是猜测,难免会有误会。对于isKindOfClass的实现很好奇,于是决定探究一下isKindOfClass的内部实现!
int main(int argc, const char * argv[]) {
//测试1
if ([[NSString class] isKindOfClass:[NSString class]]) {
NSLog(@"YES");
}else{
NSLog(@"NO");
}
//测试2
if ([[NSObject class] isKindOfClass:[NSObject class]]) {
NSLog(@"YES");
}else{
NSLog(@"NO");
}
return 0;
}
2016-07-21 08:40:48.020 TestIsKindOfClassDemo[13712:616567] NO
2016-07-21 08:40:48.021 TestIsKindOfClassDemo[13712:616567] YES
Program ended with exit code: 0
是不是很好奇测试1
的结果是NO,而测试2
得结果是YES?
很好奇isKindOfClass
的内部实现是如何实现的,但苦于上搜索索没有结果(其实之后找到了源码,链接在文章结尾)于是决定反汇编isKindOfClass
函数所在的库–libobjc.dylib
如何知道是在这个动态库中?
在Xcdoe中,进入isKindOfClass
的头文件,通过实例方法-isKindOfClass
所在的头文件路径,不难可以猜测:类方法+isKindOfClass
也是在objc这个库中!!!
通常我们在使用第三方库或者自己编写的库时,头文件和库文件都是放在一起的。但是在浏览iOS文件系统时,我们很快就会发现一个问题:/System/Library/Frameworks
、/System/Library/PrivateFrameworks
等目录下,怎么没有库文件?翻了一下书本,发现
从iOS3.1开始,包括frameworks在内的许多库文件被存进了一个大cache里,这个cache位于
/System/Library/Caches/com.apple.dyld/
可以使用KenyTm开发的dyld_decache将其中的二进制文件提取出来摘自小黄书–《iOS应用逆向工程》
打开iFunBox导出对应Frameworks的缓存文件(不能用scp)。选择dyld_shared_cache_armv7s
右键–>复制到Mac
。(之所以选择armv7s是因为发现下载下来工具dyld_decache
不支持64位。)
dyld_decache -o ./ /Users/fenglihai/Desktop/IOS逆向/导出系统framework/dyld_shared_cache_armv7s
使用file
命令确认是一个动态库文件!
fenglihaideMacBook-Pro:~ fenglihai$ file /Users/fenglihai/Desktop/IOS逆向/导出系统framework/导出后文件/usr/lib/libobjc.dylib
/Users/fenglihai/Desktop/IOS逆向/导出系统framework/导出后文件/usr/lib/libobjc.dylib: Mach-O dynamically linked shared library arm
fenglihaideMacBook-Pro:~ fenglihai$
把libobjc.dylib
拖拽到Hopper Disassembler
中,并搜索isKindOfClass
。
2fec5594 push {r4, r7, lr}
2fec5596 add r7, sp, #0x4
2fec5598 mov r4, r2
2fec559a bl _object_getClass
2fec559e b 0x2fec55a2
2fec55a0 ldr r0, [r0, #0x4]
2fec55a2 cbz r0, 0x2fec55ac
2fec55a4 cmp r0, r4
2fec55a6 bne 0x2fec55a0
2fec55a8 movs r0, #0x1
2fec55aa pop {r4, r7, pc}
2fec55ac movs r0, #0x0
2fec55ae pop {r4, r7, pc}
push {r4, r7, lr}
序言套路,从右到左入栈,保存调用者的下一条要执行的指令(lr)、栈桢(r7)以及其他特殊寄存器(r4)以便恢复。
lr,链接寄存器(link register )存放调用者的下一条指令。
r7,栈帧指针(Frame Pointer)存放调用者的栈桢(也就是栈底)。
r4,通用寄存器。
add r7, sp, #0x4
mov r4, r2
将isKindOfClass的参数aClass存放到r4中。oc中的函数会隐含两个参数:self和_cmd,r0存放参数self,r1存放参数_cmd。
bl _object_getClass
调用object_getClass方法,并设置lr寄存器,返回结果存放在r0中。从object_getClass的汇编代码可以看出,r0作为object_getClass的参数,r0也就是self。
_object_getClass:
2fea9a04 cmp r0, #0x0
2fea9a06 ite ne
2fea9a08 ldrne r0, [r0]
2fea9a0a moveq r0, #0x0
2fea9a0c bx lr
2fea9a0e nop
b 0x2fec55a2
跳转到0x2fec55a2地址。
ldr r0, [r0, #0x4]
将r0 + 4 的内容存放到寄存器r0中。由第4条指令可以知道r0 = object_getClass(self) 的首地址; 那么r0+4 即偏移4个字节: object_getClass(self)->super_class。此时r0 = [object_getClass(self) superclass]
id 以及 Class 的定义:
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
struct objc_class {
struct objc_class * isa;
struct objc_class * super_class; /*父类*/
const charchar *name; /*类名字*/
long version; /*版本信息*/
long info; /*类信息*/
long instance_size; /*实例大小*/
struct objc_ivar_list *ivars; /*实例参数链表*/
struct objc_method_list **methodLists; /*方法链表*/
struct objc_cache *cache; /*方法缓存*/
struct objc_protocol_list *protocols; /*协议链表*/
};
cbz r0, 0x2fec55ac
判断r0是否等于0,即if([object_getClass(self) superclass] == 0)。是则执行地址0x2fec55ac所在的指令。否则执行第8条指令
cmp r0, r4
比较r0 和 r4 。r0此时为:[object_getClass(self) superclass],r4即为isKindOfClass函数的参数aClass
bne 0x2fec55a0
如果(r0 != r4)跳转到 0x2fec55a0地址所在指令,如果相等继续执行第10条指令
movs r0, #0x1
将立即数1赋值给r0。r0用作函数返回。
pop {r4, r7, pc}
结语套路,恢复r4,和r7,并把lr中的值赋给程序计数器pc.。
movs r0, #0x0
将立即数0赋值给r0。r0用作函数返回。
pop {r4, r7, pc}
结语套路,恢复r4,和r7,并把lr中的值赋给程序计数器pc.
char +[NSObject isKindOfClass:](void * self, void * _cmd, void * arg2) {
r4 = arg2;
r0 = _object_getClass();
loc_2fec55a2:
if (r0 == 0x0) goto loc_2fec55ac;
goto loc_2fec55a4;
loc_2fec55ac:
r0 = 0x0;
return r0;
loc_2fec55a4:
if (r0 != r4) goto loc_2fec55a0;
goto loc_2fec55a8;
loc_2fec55a0:
r0 = *(r0 + 0x4);
goto loc_2fec55a2;
loc_2fec55a8:
r0 = 0x1;
return r0;
}
根据上面的汇编代码,不难可以还原成object-c的代码!
//逆向反编译得到的源码
+ (BOOL) isKindOfClassITX:(Class)class
{
Class r0 = object_getClass(self);
while (1) {
if (r0 == 0) {
return 0;
}else{
if (r0 != class) {
r0 = [r0 superclass];
}else{
return 1;
}
}
}
}
int main(int argc, const char * argv[]) {
//NSString 对象
if ([[NSString class] isKindOfClassITX:[NSString class]]) {
NSLog(@"YES");
}else{
NSLog(@"NO");
}
if ([[NSString class] isKindOfClass:[NSString class]]) {
NSLog(@"YES");
}else{
NSLog(@"NO");
}
//Object 对象
if ([[NSObject class] isKindOfClassITX:[NSObject class]]) {
NSLog(@"YES");
}else{
NSLog(@"NO");
}
if ([[NSObject class] isKindOfClass:[NSObject class]]) {
NSLog(@"YES");
}else{
NSLog(@"NO");
}
return 0;
}
[NSObject isKindOfClass]
的结果一致!2016-07-21 15:55:16.993 TestIsKindofCalss[14377:764296] NO
2016-07-21 15:55:16.994 TestIsKindofCalss[14377:764296] NO
2016-07-21 15:55:16.994 TestIsKindofCalss[14377:764296] YES
2016-07-21 15:55:16.994 TestIsKindofCalss[14377:764296] YES
逆向到这里,已经满足了我对isKindOfClass
内部实现的好奇心。但是,但是,为什么[[NSObject class] isKindOfClass:[NSObject class]]
的结果是YES?这个问题貌似还没有答案!
给+ (BOOL) isKindOfClassITX:(Class)class
添加注释,代码如下
//逆向反编译得到的源码
+ (BOOL) isKindOfClassITX:(Class)class
{
Class r0 = object_getClass(self);
while (1) {
if (r0 == 0) {
return 0;
}else{
NSLog(@"class->%@:%p",NSStringFromClass(class), class);
NSLog(@"r0->%@:%p",NSStringFromClass(r0), r0);
if (r0 != class) {
r0 = [r0 superclass];
}else{
return 1;
}
}
}
}
Class r0 = object_getClass(self)
r0 = [r0 superclass];
在调试中,可以发现在while循环的第二次中,return 1了。也就是说以下的代码会成立!
if ([NSObject class] ==[ object_getClass([NSObject class]) superclass]) {
NSLog(@"yes");
}
//输出结果
2016-07-21 16:59:18.790 TestIsKindofCalss[14545:795562] yes
Program ended with exit code: 0
由此,已经清楚为什么`[[NSObject class] isKindOfClass:[NSObject class]]`的结果是YES了。
从[NSObject class] ==[ object_getClass([NSObject class]) superclass]
等式成立可以得到结论:NSObject作为root对象 ,root 对象 的mateclass 的superclass == root 对象的class。
好吧,文字总是那么乏力,有图有真相!
instance object,class,metaclass 的 isa 与 super_class 关系图
在这里我写了四个方法,通过这四个方法不断尝试可以验证上述关系图。
//显示类关系
NSArray * showClassRelationship(Class currentClass)
{
NSMutableArray *classList = [[NSMutableArray alloc] initWithCapacity:0];
NSLog(@"--------------class关系--------------");
Class preClass = nil;
while (currentClass != preClass) {
[classList addObject:currentClass];
NSLog(@"%@:%p", [NSString stringWithCString:class_getName(currentClass) encoding:NSUTF8StringEncoding],currentClass);
preClass = currentClass;
currentClass = object_getClass(currentClass);
}
return classList;
}
//显示继承关系
NSArray * showInheritRelationship(Class currentClass)
{
NSMutableArray *classArray = [[NSMutableArray alloc] initWithCapacity:0];
NSLog(@"--------------继承关系--------------");
while (currentClass) {
[classArray addObject:currentClass];
NSLog(@"%@:%p", [NSString stringWithCString:class_getName(currentClass) encoding:NSUTF8StringEncoding],class_getSuperclass(currentClass));
currentClass = class_getSuperclass(currentClass);
}
return classArray;
}
/*
* 显示实例方法列表.
* 通过遍历currentClass的isa指针,输出所有的实例方法
*/
void showInstanceMethodList(Class currentClass)
{
NSArray *classList = showInheritRelationship(currentClass);
int j = 0;
for (Class currentClass in classList) {
unsigned int methodCount;
Method *methodList = class_copyMethodList(currentClass, &methodCount);
unsigned int i = 0;
NSLog(@"-------%d-------MethodList %@:%p--------------",j, [NSString stringWithCString:class_getName(currentClass) encoding:NSUTF8StringEncoding],currentClass);
for (; i NSLog(@"%d:%@ ", i,[NSString stringWithCString:sel_getName(method_getName(methodList[i])) encoding:NSUTF8StringEncoding]);
}
free(methodList);
j++;
}
}
/*
* 显示类方法列表.
* 通过遍历currentClass的isa的isa指针,输出所有的类方法
*/
void showClassMethodList(Class currentClass)
{
NSArray *classList = showInheritRelationship(currentClass);
int j = 0;
for (Class currentClass in classList) {
unsigned int methodCount;
Method *methodList = class_copyMethodList(object_getClass(currentClass), &methodCount);
unsigned int i = 0;
NSLog(@"-------%d-------MethodList %@:%p--------------",j, [NSString stringWithCString:class_getName(currentClass) encoding:NSUTF8StringEncoding],currentClass);
for (; i NSLog(@"%d:%@ ", i,[NSString stringWithCString:sel_getName(method_getName(methodList[i])) encoding:NSUTF8StringEncoding]);
}
free(methodList);
j++;
}
} NSLog(@"--------------class关系--------------");
Class preClass = nil;
while (currentClass != preClass) {
[classList addObject:currentClass];
NSLog(@"%@:%p", [NSString stringWithCString:class_getName(currentClass) encoding:NSUTF8StringEncoding],currentClass);
preClass = currentClass;
currentClass = object_getClass(currentClass);
}
return classList;
}
调用一下方法,查看NSString的class、metaclass、继承关系以及方法列表信息
showMethodList([NSString class]);
输出结果
2016-07-22 00:30:06.500 TestIsKindofCalss[14915:869373] --------------class关系--------------
2016-07-22 00:30:06.501 TestIsKindofCalss[14915:869373] NSString:0x7fff726696d8
2016-07-22 00:30:06.501 TestIsKindofCalss[14915:869373] NSString:0x7fff72669700
2016-07-22 00:30:06.501 TestIsKindofCalss[14915:869373] NSObject:0x7fff7269d118
2016-07-22 00:30:06.502 TestIsKindofCalss[14915:869373] --------------继承关系--------------
2016-07-22 00:30:06.502 TestIsKindofCalss[14915:869373] NSString:0x7fff7269d0f0
2016-07-22 00:30:06.502 TestIsKindofCalss[14915:869373] NSObject:0x0
Program ended with exit code: 0
NSString:0x7fff726696d8
NSString的class 对于关系图的第2列NSString:0x7fff72669700
NSString的metaclass 对于关系图的第3列NSObject:0x7fff7269d118
NSString的rootclass 对于关系图的第3列,也就是NSObject的metaclass。调用showClassRelationship([NSObject class]);
打印NSObject的class关系,可以发现NSObject的metaclass的地址和NSString的rootclass的地址一致!
2016-07-21 23:58:08.515 TestIsKindofCalss[14791:851749] --------------class关系--------------
2016-07-21 23:58:08.516 TestIsKindofCalss[14791:851749] NSObject:0x7fff7269d0f0
2016-07-21 23:58:08.517 TestIsKindofCalss[14791:851749] NSObject:0x7fff7269d118
NSString:0x7fff7269d0f0
自身是NSString类NSObject:0x0
父类是NSObjectshowClassMethodList
和showInstanceMethodList
函数查看类方法和实例方法!