在第3章中介绍Theos时,已经介绍了tweak的概念。依据维基百科的定义,tweak指的是对电子系统进行轻微调整来增强其功能的工具;在iOS中,tweak特指那些能够增强其他进程功能的dylib,是越狱iOS的最重要组成部分。
正是因为tweak的存在,越狱iOS用户才能依照自己的喜好打造独一无二的个性化系统,iOS开发者才有机会站在优秀软件的肩膀上为它们添砖加瓦,丰富它们的功能,而这些便利都是原版iOS和AppStore无法提供的。Cydia中最受欢迎的软件几乎全是创意各异的tweak(图5-1是Cydia中的tweak图标),如Activator、Barrel、SwipeSelection等。一般来说,一个tweak的核心是各种“hook”,而绝大部分的hook是针对Objective-C方法的,那么tweak是如何工作的呢?
图5-1 tweak图标
Objective-C是典型的面向对象语言。iOS是由一个个小的组件构成的,这些组件其实就是一个个对象。举个例子,iOS里的每个图标、每条信息和每张照片都是对象;除了这些用户能够看到的对象以外,还有很多对象一直在后台工作,为前台对象提供各种支持。例如有些对象负责与苹果的服务器通信,有的对象负责读写文件。一个对象可以拥有其他对象,例如图标对象就拥有一个标签对象,用来显示这个图标代表的App名称。一般来说,每个对象都有自己存在的意义,工程师通过对不同对象的组合排序,实现不同的功能;在Objective-C里,我们称对象的功能为“方法”,“方法”的具体行为则称为“实现”。对象、方法和实现的关系,就是tweak大做文章的地方。
对象具备了某种功能,代码就可以发出指令“[object method]”,让一个对象去执行它的功能,也就是“调用对象的方法”。看到这里,可能有朋友会说,指令里的“对象”和“方法”都是名词,而执行一个功能需要的不应该是一个动词吗?说得没错,我们还缺少一个动词,需要去“实现”这个方法。需要的动词已经出现了——“实现”,它指的是当某个方法得到调用时,iOS实际干了些什么,也就是执行了什么代码。在Objective-C里,方法和实现的关系不是在编译时决定的,而是在运行时决定的。
在实际使用中,“[object method]”中的method不一定是一个名词,它也可能是一个动词。但仅凭简短的[object method],还是不知道要怎么实现这个方法,比如:“妈妈,接一下电话”,翻译成Objective-C语言是“[妈妈接电话]”,这里的对象是妈妈,方法是“接电话”,实现是“放下手里的炒菜铲子,把炉火关小一点,然后走到客厅去接电话”;“snakeninny,过来搬个东西”,翻译成Objective-C语言是“[snakeninny搬东西]”,这里的对象是snakeninny,方法是“搬东西”,实现是“停下手里的工作,从椅子上起来,走到老板的办公室里把一个箱子抬到楼下”。上面的两个例子如果没有“实现”的具体描述,即使调用了“方法”,“对象”也不知道具体该干嘛。“实现”是“方法”的释义,“方法”是词语,“实现”是词语的意义——这不就是词典吗?
随着时代的进步,词典的内容产生了变化,一些旧词语被赋予了新解释,“灌水”跟液体已经没有太大关系,“粉丝”也从一种食物变成了一类人。这些现象在iOS中也有体现,我们可以通过改变“实现”和“方法”的对应关系,赋予一个方法新的意义,从而达到更改对象功能的目的;只要别人在查询一个词语意义的时候参考了你修改过的词典,那么他的方法就有了新的实现,例如,笔者开发的LowPowerBanner(如图5-2所示)会在低电量时以横幅代替弹窗,提醒用户没电了——哈哈,那正是因为笔者更改了低电量提醒的实现,善意地欺骗系统“弹窗”的意思是“横幅”。
笔者的另一个短信防火墙SMSNinja(如图5-3所示)能在收到垃圾短信时将其自动放进垃圾箱,这是通过更改iOS收到短信的动作实现的,在原有基础上增加了检测垃圾短信的功能。这种“更改词典内容”的方式就是通过CydiaSubstrate进行hook操作来实现的。CydiaSubstrate的用法已经在前两章详细介绍过了,想必大家都还记得。
图5-2 LowPowerBanner
图5-3 SMSNinja