【技术分享】如何使用Frida绕过iOS应用的越狱检测

https://p3.ssl.qhimg.com/t01b059c3b14e692cd1.png

翻译:興趣使然的小胃

预估稿费:180RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


一、前言

在这篇文章中,我们将向读者介绍Frida的一些相关知识。Frida是移动应用安全分析中的一个非常有趣的工具。

即使你从来没有用过Frida,你也可以将这篇文章作为使用Frida开展移动应用安全研究和探索的指南。

这篇文章主要包含以下内容:

1、Frida简介

2、如何在iOS上配置Frida

3、如何使用Frida连接到某个iOS进程

4、如何导出类和方法信息

5、如何使用Frida操控正在运行的iOS应用

6、总结


二、Frida简介

Frida是一个跨平台的动态代码注入工具集,你可以使用它hook应用程序,插入自己的JavaSciprt代码,同时也能获取应用的内存和函数的完全访问权限。

Frida由Ole André V. Ravnås(@oleavr)所开发,有个非常活跃的IRC(Internet Relay Chat,互联网中继聊天)频道与之相关,你可以在这个频道中与其他Frida爱好者探讨思路、问题以及工具的新功能。IRC频道为irc.freenode.net上的#frida频道。

根据具体的使用需求,Frida的一些应用场景为:

1、Hook某个函数,修改返回值。

2、分析自定义协议,嗅探或解密实时流量。

3、调试自己的应用程序。

4、导出iOS应用的类和方法信息。

5、其他应用场景。

我之所以提到“5、其他应用场景”,是因为我们可以将Frida用于各种用途。Frida提供的API功能非常强大,是构建自己的安全或分析工具的首选方案。现在已经有几种工具以Frida为基础,比如Needle以及AppMon等。

Firda的其他应用场景中就包括它可以在非越狱设备上工作,这一点非常有用。为了在非越狱设备上运行Frida调试应用程序,我们需要使用诸如Swizzler2的工具修改应用程序,将FridaGadget dylib添加到应用程序中。


三、在iOS上配置Frida

只需要简单几步,我们就能配置Frida分析iOS应用的安全性。我们需要在iOS设备以及主机上进行配置。

为了在iOS设备上安装Frida,我们需要按照以下步骤操作:

1、在iOS设备上打开Cydia应用。

2、添加源,地址为“https://build.frida.re/”

http://p5.qhimg.com/t01be05086fea62eeff.png

3、进入已添加的源,搜索“Frida”,点击“Modify”然后安装。

http://p6.qhimg.com/t0111a3b465d9668083.png

要在主机上安装Frida,我们需要启动控制台,输入“pip install frida”命令,安装Frida程序。


四、使用Frida连接iOS进程

Frida安装完毕后,现在我们已经准备就绪,可以开始使用Frida评估iOS应用的安全性的可利用性。

我们的目标应用是Prateek Gianchandani开发的DVIA(Damn Vulnerable Ios Application,这个应用专门用于iOS平台的渗透测试)iOS应用。本文中用到的脚本大多数都可以在Interference Security的Github代码库中找到。

我们将要分析的是DVIA提供的越狱检测功能,如下所示,该应用提示设备目前处于越狱状态:

http://p7.qhimg.com/t0176f575831b3864f2.png

我们可以先看一下目标设备当前运行的进程列表,命令如下:

frida-ps –U

http://p7.qhimg.com/t0151f4fc011089e9ee.png

从上图中,我们可以了解到iOS设备当前正在运行的所有进程。

我们可以使用“frida –U process-name”命令,将Frida附加到任意一个进程上,命令执行成功后我们将会跳转到Frida控制台中,可以访问目标进程的所有属性、内存内容以及函数功能。

http://p0.qhimg.com/t0102d944385bc0eb55.png

我们可以使用Frida的shell与目标进程交互,也可以编写自己的JavaScrpt代码,获取需要分析的数据。


五、导出iOS应用的类和方法信息

对于DVIA的越狱检测功能而言,我们的目标是确定哪个ViewController和函数负责检测设备的越狱状态。

首先,我们可以写个基本的Frida脚本,导出目标应用中的所有类和方法,从中找到与越狱有关的所有内容,以便在Frida的帮助下绕过应用的越狱检测。

我们的工作流程如下所示:

http://p1.qhimg.com/t01917ce7200782f68f.png


六、使用Frida找出DVIA中负责越狱检测的类

首先我们先找出应用中的所有类。

for (var className in ObjC.classes)
    {
        if (ObjC.classes.hasOwnProperty(className))
        {
            console.log(className);
        }
}

运行脚本,将Frida附加到目标进程中(如下图所示),我们可以从运行结果中,找到目标进程的所有类。

http://p9.qhimg.com/t01d8b1b85e76c0c26b.png

我们最好通过grep命令搜索目标类,在本文的案例中,我们可以使用“Jailbreak”这个关键词。

通过关键词搜索,我们定位到一个名为“JailbreakDetectionVC”的类,如下图所示:

http://p3.qhimg.com/t016240153ed53dd34c.png

找到所有实例后,你可能会看到一个错误提示,忽略这个提示即可。

现在我们已经找到目标类,接下来我们可以探索这个类中的关键函数。


七、使用Frida找出DVIA中负责越狱检测的方法

为了查找类方法,我们需要使用Frida提供的“ObjC.classes.class-name.$methods”方法。在本文案例中,我们只需要查找“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");

运行这个脚本,使用grep命令查找类似“Jailbreak”、“Jailbroken”、“Detection”之类的关键词,如下图所示:

http://p0.qhimg.com/t01b6cc1d8977054b0d.png

根据这些关键词,我们找到了三个方法,分别为“isJailbroken”、“jailbreakTest1Tapped:”以及“jailbreakTest2Tapped:”。

对于本文案例,“isJailbroken”看上去更像是检测设备是否越狱的方法。


八、使用Frida修改DVIA中负责越狱检测方法的返回值

现在我们可以分析“isJailbroken”方法究竟发送了什么类型的返回值。

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[-] Return Value: " + retval);
          }
        });
    }
    catch(err)
    {
        console.log("[!] Exception2: " + err.message);
    }
}
else
{
    console.log("Objective-C Runtime is not available!");
}

运行这个脚本,点击iOS应用中的“Jailbreak Test 1”按钮,之后我们就可以在Frida控制台中看到函数的返回值。

http://p6.qhimg.com/t0179fe6813e861f235.png

由于我们的设备已越狱,我们获得的返回值为0x1,表明函数的返回结果为True。

接下来我们的任务是覆盖这个返回值,修改这个方法,使得不管什么时候我们点击应用中的“Jailbreak Test 1”按钮,函数的返回值始终为false(即0x0)。

我们只需要添加一行代码,就可以修改这个函数的返回值。

我们可以通过如下代码,修改函数的返回值,并将返回值反馈到控制台中:

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!");
}

运行这个脚本,我们可以在控制台中看到函数的返回值已经被成功修改:

https://p0.ssl.qhimg.com/t0108f70e1cb5f1d2e8.png

回头看看这个iOS应用,你会发现应用提示设备处于未越狱状态,如下所示:

https://p1.ssl.qhimg.com/t011eb83e0327b9da96.png


九、总结

这就是本文的全部内容。在接下来的文章中,我们会向大家介绍Frida脚本语言的详细知识,以及如何利用Frida的API函数和其他工具对iOS和Android应用的安全性进行评估。

(完)