Frida——移动应用程序进行安全分析的主要工具,即使你从未使用过Frida,读完这篇文章,你也将对它有充分的了解。
Frida介绍
Frida是一个动态的代码工具包,它是一款基于python + javascript 的hook与调试框架,相比xposed和substrace cydia更加便捷,可以向Windows,Mac,Linux,iOS和Android上的app注入javascript代码片段,并完全访问程序的内存。
根据你使用的目的,Frida的一些使用场景如下:
1.hook特定函数并更改返回值,
2.分析特定协议,并迅速探测流量
3.根据你的需求,可以对应用程序进行适当地调试
4.从iOS应用程序中提取dumping类和类方法信息
……
总之 Frida的应用范围是非常的广泛,功能也非常的强大,是建立自己的安全或分析工具的首选。目前已经有几种基于Frida的安全分析工具,包括Needle和AppMon。
Needle是一个开源的模块化框架,主要简化iOS应用程序安全评估过程,并作为一个中心点。鉴于其模块化方法,Needle很容易扩展新模块,可以以Python脚本的形式加入。
AppMon可以通过二进制指令获取app程序运行记录,并显示调用函数和相关参数。其次,还包括了一系列app事件监控和行为修改脚本,并能通过web接口显示和操作。
多平台动态框架环境Frida之所以如此的能对安全检测如此有用,就是因为它能够在非越狱的设备上运行。 Frida提供了一个叫做“FridaGadget.dylib”的动态库,可以用来在未越狱设备上为应用程序插入FridaGadget.dylib。你可以使用Swizzler2等工具来修改应用程序以在应用程序中添加FridaGadget.dylib。
添加FridaGadget.dylib后,你就可以获取开发人员配置的文件和证书,配置文件是由苹果签名的、将一个或多个设备上的代码签名证书列入白名单的plist文件。换言之,这是苹果明确允许你的应用程序在某些上下文中运行,比如在选定设备的调试模式下。配置文件还列出了授予你的应用程序的权限。
设置Frida
在iOS应用程序中设置Frida是非常简单的,首先就是在你的iOS设备上安装Frida服务器,步骤如下。
1.在你的iOS设备上打开Cydia应用程序,
2.添加一个URL,URL为https://build.frida.re
3.打开Source或搜索Frida,然后单击Modify,最后单击Install。
同时,你还可以使用Python绑定来完成更复杂的任务,可以使用pip install frida来完成对Python的绑定。
使用Frida连接到iOS进程
Frida现在已经设置完成,可以开始使用了。本文利用了iOS安全专家Prateek Gianchandani的Damn Vulnerable iOS Application(DVIA)方法,具体的原理请点击此处了解。
接下来,我们将分析DVIA是如何对越狱环境进行检测的,如下图所示,该设备已经越狱了。
我们先查看一下这个越狱设备上的所有正在运行的进程列表,
frida-ps –U
从上图可以看出,我们现在已经查到了该iOS设备上运行的所有进程的列表。
现在就可以试着利用一个frida –U process-name把frida加载到其中任意一个进程中了,如下图所示,现在我们就可以在frida控制台中,访问所有目标进程的属性,内存内容和相关功能。
现在,我们即可以在Frida的shell中工作,并与iOS设备上进程进行交互,也可以编写自己的JavaScript来获取我们想要的数据。
dump出class文件并method出信息
我们可以利用此操作来确定利用DVIA越狱时,哪个ViewController和function负责验证iOS设备是否越狱。
ViewController是iOS应用程序中重要的部分,是应用程序数据和视图之间的重要桥梁,ViewController管理应用中的众多视图。 iOS的SDK中提供很多原生ViewController,以支持标准的用户界面,例如表视图控制器(UITableViewController)、导航控制器(UINavigationController)、标签栏控制器(UITabbarController)和iPad专有的UISplitViewController等。
先来写一个基本的Frida脚本来dump目标应用程序中存在的所有各种class文件和method信息。此时,我们就可以开始寻找与越狱相关的任何内容了,然后利用Frida绕过越狱检测,如下图所示,就是整个过程的示意图。
用DVIA方法查找Frida可以绕过哪些越狱检测
我们先来看看应用程序中的所有class文件。
for (var className in ObjC.classes) { if (ObjC.classes.hasOwnProperty(className)) { console.log(className); } }
一旦运行以上这些指令, Frida就会附加到目标进程,一旦加载完成,它将在目标进程中显示出许多class文件。如下图所示,我们可以在iOS设备上,利用Frida管理JavaScript,
当我们使用grep命令来运行上述越狱命令时,我们会看到一个JailbreakDetectionVC的class文件,如下图所示,我们就是用Frida来识别iOS应用程序的目标class文件。
用DVIA进行越狱检测的方法
我们需要使用ObjC.classes.class-name.$methods才能找到进行越狱检测的方法。本文中,我们只介绍查找我们的目标class文件的方法,即使用JailbreakDetectionVC。
console.log("[*] Started: Find All Methods of a Specific Class"); if (ObjC.available) { try { var className = "JailbreakDetectionVC"; var methods = eval('ObjC.classes.' + className + '.$methods'); for (var i = 0; i < methods.length; i++) { try { console.log("[-] "+methods[i]); } catch(err) { console.log("[!] Exception1: " + err.message); } } } catch(err) { console.log("[!] Exception2: " + err.message); } } else { console.log("Objective-C Runtime is not available!"); } console.log("[*] Completed: Find All Methods of a Specific Class");
运行以上的命令后,如下所示,同时利用Jailbreak,Jailbroken 和Detection的grep,来进行精确字符串 查找。
如上图所示,我们发现Jailbroken,jailbreakTest1Tapped:和jailbreakTest2Tapped:含有我们要查找的目标。
其中isjailbroken看起来像是检测设备是否越狱,并最有可能发送返回值。
利用DVIA修改Frida越狱检测方法的返回值
让我们来看看Jailbroken会发送什么样的返回值。
console.log("[*] Started: Find All Methods of a Specific Class"); if (ObjC.available) { try { var className = "JailbreakDetectionVC"; var methods = eval('ObjC.classes.' + className + '.$methods'); for (var i = 0; i < methods.length; i++) { try { console.log("[-] "+methods[i]); } catch(err) { console.log("[!] Exception1: " + err.message); } } } catch(err) { console.log("[!] Exception2: " + err.message); } } else { console.log("Objective-C Runtime is not available!"); } console.log("[*] Completed: Find All Methods of a Specific Class");
运行此命令后,请在iOS应用程序中选择Jailbreak Test 1按钮,这样我们就能在Frida控制台中看到显示的返回值。
由于我们的设备此时已经越狱,所以它显示的返回值为0x1,这意味着函数返回为True。
接下来,我们将修改此返回值并修改该方法,以便以后在应用程序中选择Jailbreak Test 1按钮时,它将返回false或0x0。
可以通过添加一行代码来改变这个特定函数的返回值,如下图所示该代码行可以更改返回值并将其记录到Frida控制台。
newretval = ptr("0x0") retval.replace(newretval) console.log("t[-] New Return Value: " + newretval)
最后的代码如下,
if (ObjC.available) { try { var className = "JailbreakDetectionVC"; var funcName = "- isJailbroken"; var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]'); Interceptor.attach(hook.implementation, { onLeave: function(retval) { console.log("[*] Class Name: " + className); console.log("[*] Method Name: " + funcName); console.log("t[-] Type of return value: " + typeof retval); console.log("t[-] Original Return Value: " + retval); newretval = ptr("0x0") retval.replace(newretval) console.log("t[-] New Return Value: " + newretval) } }); } catch(err) { console.log("[!] Exception2: " + err.message); } } else { console.log("Objective-C Runtime is not available!"); }
一旦我们运行上面这些命令,就可以看到返回值已经被修改,如下图所示。
如果现在再查看我们的iOS应用程序,则会发现该设备没有越狱,如下图所示。