相对于前几章的示例来说,本章展示的逆向工程的整体思路虽未变,但复杂程度增大了不少,尤其是与同为系统App的第7章Notes相比,两者的难度相差了若干数量级。为了逆向出看似很简单的iMessage检测和发送操作,大致的思路是下面这样的。
(1)从表面现象入手
“Text Message”变成“iMessage”,绿色变成蓝色,“Send”按钮,这些表层现象都来自于底层代码。只要能描述出所观察到的东西,就可以以此入手,开始逆向分析。本章,就是从信息输入框的占位符和“Send”按钮入手,通过Cycript定位到其实现代码,并切入代码层的。
(2)浏览class-dump出的头文件,找到感兴趣的点
Objective-C头文件结构清晰,函数名的含义明确,可读性强,是寻找蛛丝马迹的理想场所。用Cycript对那些简单的函数、属性、实例变量做测试,有助于我们对类的功能有大体了解。本章,在获取一些重要变量的时候,并没有严谨地借助IDA和LLDB这两样大杀器,而是仅靠阅读头文件,通过函数名猜测参数的类型与用法,配合Cycript进行测试,最终得出了理想的结果。黑猫白猫,抓到老鼠的就是好猫。
(3)在IDA中查看函数是如何形成一个面的
在查看函数内部实现时,IDA无疑是最好用的神器之一。不管是通过交叉引用、地址跳转,还是全局搜索,都可以快速定位关键词,并方便地浏览上下文,对关键词的前因后果有准确的把握。在检测iMessage时,我们用IDA理顺了[CKMessageEntryView updateEntryView]、[CKPendingConversation sendingService]、[CKPendingConversation composeSendingService]和IMChatCalculateServiceForSendingNewCompose等函数的调用关系,其中IMChatCalculateServiceForSendingNewCompose是一个C函数,对class-dump免疫;在发送iMessage时,从上层的[CKTranscriptController sendComposition:CKComposition],一路经过[CKTranscriptController_startCreatingNewMessageForSending:]、[CKConversation sendMessage:newComposition:]、[CKConversation sendMessage:onService:newComposition:],追踪到了底层的[IMChat sendMessage:IMMessage],都是依靠IDA提供的关键词及关联性从一个面中,手工地把那条线给挑出来的。虽然没有机器自动完成方便,但工作量也完全在可以接受的范围内,这都要归功于IDA提供的强大分析结果。
(4)用LLDB确认唯一的那条线
LLDB的使用贯穿本章的始终,即使是在有意“克制”的10.3节,我们也在寻找函数调用者、动态查看参数的时候“不得不”惊动LLDB它“老人家”。相对于GDB,LLDB对iOS的支持要好得多,基本不会出现崩溃等Bug,对于Objective-C的支持也很到位,让我们可以专注在调试本身上。在进行iMessage的检测及发送的环节中,用LLDB澄清了大量细节,通过对数据源一环扣一环的分析,基本厘清了iOS发送iMessage的一小段流程。由小见大,从中也可以窥探出苹果的设计思路:MobileSMS是一间邮局,邮局的建筑材料、办公设备和工作人员均来自ChatKit,而充当邮差工作的是IMCore。用户去邮局发一封信,他把信件放在邮筒里,由工作人员整理后交付给邮差,送信的进度和结果再由邮差反馈给工作人员,工作人员再通知用户,这样就完成了整个的服务流程闭环。三者各司其职,为果粉带来良好的用户体验;我们通过逆向工程学习到的这种设计思路,如果能融会贯通,运用到自己的产品设计里去,给产品所带来的优雅度、设计感、健壮性都将是仅仅阅读开发文档所无法企及的。