基本原理
Contents of (__DATA,__objc_classrefs) section和Contents of (__DATA,__objc_superrefs) section中的类是已经被引用的类,Contents of (__DATA,__objc_classlist) section中的类是整个应用未启动时已经定义好的类列表,那么从后者剔除前者而剩余的类就是应用未启动时没有使用的类;同时考虑到Objective-C的运行时特性,所以还需要人工确认下。
基本过程如下:
解压ipa包
本质是zip包,修改后缀解压就可以了
otool 符号格式化
在mac os shell中运行命令(otool 为Xcode工具包中的工具)
otool -arch armv7 -ov 应用解压后的目录/Payload/应用名/应用二进制文件 > otool.txt
上述命令中指定armv7架构,其假设是ipa包一定会有armv7的二进制文件
解析获取 __objc_classlist 类列表
打开 otool.txt文件,搜索__objc_classlist ,找到一个class数据,其基本格式如下
025afc00 0x28b2a24 # 第二个,地址0x28b2a24,是class的唯一地址
isa 0x28b2a38
superclass 0x28b4194 # 父类的地址
cache 0x0 __objc_empty_cache
vtable 0x0
data 0x25b5e0c
flags 0x194 RO_HAS_CXX_STRUCTORS
instanceStart 4
instanceSize 20
ivarLayout 0x2123649
layout map 0x13
name 0x2123610 BCCallMultiVideoController #类名字
baseMethods 0x25b5a88
。。。
我们需要重点关注类地址、superclass 父类地址,name 类名,按照这样的格式解析所有的__objc_classlist 从而获得所有定义好的类集合。
搜索__objc_classrefs 和__objc_superrefs,其基本格式如下
02896b94 0x0 _OBJC_CLASS_$_UIView
02896b98 0x0 _OBJC_CLASS_$_UIScrollView
02896b9c 0x0 _OBJC_CLASS_$_UIColor
02896ba0 0x0 _OBJC_CLASS_$_NSNumber
02896ba4 0x0 _OBJC_CLASS_$_UIImage
02896ba8 0x0 _OBJC_CLASS_$_NSMutableArray
02896bac 0x28b2a24 # 第二个地址,0x28b2a24,是引用的类
这样我们就获得了应用未启动时所有已经引用的类(从里面剔除系统类)
然后,就是获取__objc_classrefs、__objc_superrefs和__objc_classlist的差集,需要注意下因为继承而间接被引用的情况。例如__objc_classrefs集合中有一个0x28b2a24,而这个地址在__objc_classlist中对应BCCallMultiVideoController,所以BCCallMultiVideoController被引用了,而BCCallMultiVideoController的父类是0x28b4194,那么类0x28b4194也被引用了,这样追溯直到父类未空或者父类未系统类。