作者:姜若芾@平安银河安全实验室
公众号:https://mp.weixin.qq.com/s/zluEXsK3PkwnnoHyyCHQkg
背景
之前在《使用IDA Pro的REobjc模块逆向Objective-C二进制文件》一文中,我们提到了在使用IDA Pro分析iOS应用的过程中,由于Objective-C语言的动态特性,大部分的方法调用都是通过调用_objc_msgSend
方法加上实际调用方法名作为参数的方式进行,以至于在IDA Pro分析完成后,在交叉引用列表中缺失了许多原本应该存在的引用记录。
DUO Labs的Todd Manning开发了一款IDA Pro的脚本,可以帮助逆向研究者更全面地获取交叉引用的信息。可惜的是,这款脚本仅面向x64平台,所以如果我们分析的是iOS应用,这款工具只能补全针对模拟器编译的应用包,对于实际情况下的逆向工作还是有许多的限制。
在今年三月份举办的2019年RSA大会上,美国国家安全局(NSA)公开介绍并演示了一款名为Ghidra的逆向工程框架,并且将Ghidra作为开源工具开放给了大众使用。由于其为java编写的特点,所以可以跨平台使用,并且支持多种CPU架构。Ghidra可以对主流操作系统的二进制文件进行分析,包括Android和iOS。同时,用户可以使用开放的API接口开发自己的Ghidra插件组件或脚本。对于这样一款功能强大并且是开源的逆向工具,我们也第一时间将其使用到了日常逆向工作中。
初步分析
我们搭建了一个iOS的测试工程,选择真机设备进行编译,使其编译后的应用包为arm64的CPU架构,然后拖入Ghidra和IDA Pro进行分析。
我们创建了一个名为Lion的类,在其中创建一个名为lionFirstMethod的方法。然后在
ViewController类的method1方法中使用[lion lionFirstMethod]的方式进行调用。同时,我们在Lion类的lionSecondMethodWithArg1: andArg2:
方法中使用同样的方式进行调用。通过之前的研究我们可以知道,这些方法调用实际上都是通过_objc_msgSend
的方式进行调用。我们在IDA Pro和Ghidra中针对lionFirstMethod及其交叉引用信息进行分析,结果发现:
在IDA Pro中,仅分析出了lionSecondMethodWithArg1: andArg2:
方法中的调用。
在Ghidra中,不仅分析出了lionSecondMethodWithArg1: andArg2:
方法中的调用,而且还分析出了另外一处method1中的调用。
这不禁引起了我们的注意和兴趣,经过分析我们发现,Ghidra解析出了测试工程中大部分的_objc_msgSend
调用,而IDA Pro只解析出了一部分。
解析_objc_msgSend
对于逆向工程来说,准确并全面的交叉引用信息尤为重要,它可以帮助我们分析某个关键函数在二进制文件中是否被调用以及如何被调用。基于这点,我们开始研究在Ghidra中编写Python脚本来分析所有的_objc_msgSend
方法,试图解析出实际调用的类和方法名。
首先我们需要找出_objc_msgSend
,于是我们遍历可执行文件中所有的方法,在每个方法中遍历arm指令,如果遇到bl指令,判断其是否是_objc_msgSend
。由于在bl指令调用_objc_msgSend
方法前,处理器会读取内存数据,向寄存器中写入类、方法名和参数信息,所以如果是_objc_msgSend
,我们就可以按地址往前寻找ldr指令。
上面是一个非常简单的例子,这里可以看到,程序在地址0x100006134处调用了_objc_msgSend
,而此处实际调用的方法是[Lion alloc]。我们按地址往前寻找,可以看到在0x100006128和0x10000612c处都使用了ldr指令从内存中读取了数据写入了寄存器。根据arm的ABI文档,我们可以知道,在函数调用时,前几个寄存器会被用来存放函数的参数。所以对于没有参数的[Lion alloc]来说,Lion和alloc便是_objc_msgSend
函数的两个参数,我们也可以在arm指令中看出,在bl指令调用_objc_msgSend
之前,指向类Lion和方法alloc的引用分别被写入了X0和X1寄存器。
在找到ldr指令后,我们可以分析ldr指令对应的操作数,如果对应到的是一个引用,就可以提取其引用信息。通过递归跟踪其引用地址,可以分析出其最终指向的内容。如果指向的内容是类名,那我们就找到了此次_objc_msgSend
调用对应的类信息,如果指向的内容是方法名,那我们就找到了此次_objc_msgSend
调用对应的方法信息。以此,我们就可以拼凑出该_objc_msgSend
对应的实际方法调用。
完善脚本
在编写脚本的过程中,我们发现,ldr指令中对应的操作数,其最终指向的内容多为__objc_methname
和__objc_classname
段中的数据。因此,我们在真正开始分析之前,先要解析这两个段中的数据并保存起来,以便解析_objc_msgSend
的时候可以快速地对映到相关类名和方法名。
此外,由于Ghidra在初始分析完成之后,交叉引用信息已经比较完善,并且在大部分的_objc_msgSend
调用处都已经添加了对应分析完成的注释,所以我们可以将这两类信息一并加以分析并辅助我们的分析结果。
脚本编写完成后可以发现,通过脚本解析的_objc_msgSend
的类名和方法名与Ghidra分析的类名和方法名一致。
此外,由于Objective-C动态的特性,有一些诸如”performSelector”这样的反射方法。我们编写脚本的时候也考虑到了这个特点,如果分析得到的方法名为performSelector,我们会追加分析其参数,以便解析出真正的调用方法。
在脚本编写完成后,我们尝试在测试工程中运行脚本。共解析出93处交叉引用,其中通过脚本补全了其中的18处。相同的测试工程在IDA Pro分析后仅解析出交叉引用共14处。
目前该脚本已经在Github上开源:https://github.com/PAGalaxyLab/ghidra_scripts/blob/master/AnalyzeOCMsgSend.py
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1037/