iOS APP 逆向安全杂谈之三

图3 使用itools查看fstab文件大小

3) 符号链接检测

IOS的磁盘被划分为两个分区:容量较小的是系统分区,另一个是比较大的数据分区。IOS预装的APP会安装在系统分区下的/Applications文件夹下,但是系统分区在设备升级时会被覆盖且容量太小,所以一些越狱工具会重定向这个目录到一个大的用户分区。通常情况下/Applications文件夹会被符号链接到/var/stash文件目录下,APP可以通过lstat函数检测/Applications的属性,如若是目录则代表未越狱,如若是符号链接则代表是越狱设备。

iOS APP 逆向安全杂谈之三_第1张图片

图4 使用itools查看符号链接

图5 使用命令行查看符号链接

(3)检测system( )函数的返回值等多种方法

这个方法来源于一个网站,同时还介绍了其他几种检测越狱的方法。其中一个方法就是检测system( )函数的返回值,调用System( )函数,不需要任何参数。在越狱设备上会返回1,在非越狱设备上会返回0。

0x04 可以动手了

我们使用文件检测的第一种方法来动手测试一下我们的越狱设备。代码如下:

#include <sys/stat.h>
#include <stdio.h>

intisJailBroken( );
intmain(){
    if(isJailBroken()){
        printf("Device is JailBroken\n");
    }else{
        printf("Device is not JailBroken\n");
    }
    return0;
}
intisJailBroken(){
   structstatbuf;
    intexist = 0;
    char* jbFiles[] =
{"/usr/sbin/sshd","/bin/bash","/Applications/Cydia.app","/private/var/lib/apt","/Libra  ry/MobileSubstrate/MobileSubstrate.dylib"};
    for(inti=0;i < sizeof(jbFiles)/sizeof(char*);i++){
        exist = stat((jbFiles[i]),&buf);
        if(exist == 0){
            return1;
        }
    }
    return0;
}

以上代码主要检测了/usr/sbin/sshd/bin/bash/Applications/Cydia.app/private/var/lib/apt/Library/MobileSubstrate/MobileSubstrate.dylib这几个文件是否存在,如果存在则证明该设备已经越狱。

iOS APP 逆向安全杂谈之三_第2张图片

图6 检测越狱的POC

在Mac下使用以下命令编译代码:

clang -framework Foundation -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk ~/Desktop/jailbreak.c -o ~/Desktop/jailbreak64  -miphoneos-version-min=5.0

编译之后将二进制文件拷贝到越狱设备当中并在命令行中运行它。下图可看到运行程序后显示设备已经越狱。

iOS APP 逆向安全杂谈之三_第3张图片

图7 赋予文件权限并执行

0x05 道高一尺魔高一丈

一些APP正是使用了类似上面的方法来检测自己所在设备是否进行了越狱,为了保证自身的安全性如果设备越狱则自动退出。是的这种做法很明智,但是这也只是增加了安全测试的难度,我们既然知道了检测越狱的原理那么就可以见招拆招了。

(1) 配置好LLDB和debugserver,ssh连接到设备,在设备上使用命令行输入debugserver -x backboard *:1234 /jailbreak64附加到jailbreak64,并开启1234端口,等待LLDB的接入。

iOS APP 逆向安全杂谈之三_第4张图片

图8 在iPad端使用debugserver附加到程序

(2) Mac端切换到~/Xcode.app/Contents/Developer/usr/bin/目录下,输入lldb,启动后输入process connect connect://iosip:1234

iOS APP 逆向安全杂谈之三_第5张图片

图9 Mac端使用LLDB连接IOS设备

(3) 使用IDA分析程序逻辑结构,这里有一点需要注意:IDA分析的二进制文件必须与LLDB调试的二进制文件相同,这样偏移前基地址、ASLR偏移、偏移后基地址才能对应得上。这里由于我的iPad对应的ARM是ARM64,所以之前在Mac上我使用arm64编译的程序,将编译的程序扔到IDA中分析,这里我使用的是IDAPro6.6中的idaq64.exe进行分析的。(这里所说的ASLR偏移指的是偏移后模块基地址-偏移前模块基地址,偏移后模块基地址:二进制文件加载到内存模块在内存中的首地址;偏移前模块基地址:模块在文件中的首地址)

iOS APP 逆向安全杂谈之三_第6张图片

图10 ARM64编译后的程序在IDA中的分析

根据上图我们可以看到地址0x100007D1C下方有两个分支,即左边的判断是设备已经越狱,而右边的判断是设备没有越狱。

之所以说这里需要注意是因为我们在使用LLDB调试程序下断点时需要偏移前基地址,而这个偏移前基地址不同场景下是不一样的。下图为ARMv7编译后使用IDA分析的结果,可以看到我们关注的地址变为了0xBE28。

iOS APP 逆向安全杂谈之三_第7张图片

图11 ARMv7编译后的程序在IDA中的分析

(4) 使用LLDB查看ASLR偏移,此时需要知道几个指令:ni执行下一条指令但不进入函数体,si执行下一条指令会进入函数体,image list列举当前所以模块。

iOS APP 逆向安全杂谈之三_第8张图片

图12 使用LLDB查看ASLR偏移

上图显示jailbreak64还未启动,现在调试还发生在dyld内部,接下来一直执行ni命令,直到输出结果出现卡顿(大约3秒左右,可以明显察觉到)。到这里不要再使用ni命令,dyld已经开始加载jailbreak64,我们使用image list -o -f查看jailbreak64的ASLR偏移。

图13 多次执行ni命令后查看jailbreak64的偏移

根据上图显示jailbreak64的ASLR偏移为0x40000,所以可以确定断点位置下在‘0x40000+0x100007D1C’处。

(5) 根据以上信息我们就可以安心的下断点了,使用br s -a 地址用来下断点,使用指令p可以打印某处的值,使用指令register write给指定的寄存器赋值。

iOS APP 逆向安全杂谈之三_第9张图片

图14 下断点

上图是在‘0x40000+0x100007D1C’处设置断点

iOS APP 逆向安全杂谈之三_第10张图片

图15 查看x0的值并重新赋值来更改程序逻辑

我们使用p命令查看一下此时x0的值,发现x0为1,使用register write命令将该x0的值改为0,然后输入命令c继续,可以看到我们成功的绕过了越狱检测,程序运行结果为设备未越狱。

在Cydia上可以安装xCon软件,据说是目前为止最强大的越狱检测绕过工具,然而我安装在我的iPad上并不适用,应该暂不支持IOS8.4吧。xCon可以绕过四种越狱检测方法(根据特定的越狱文件及文件权限判断设备是否越狱;根据沙盒完整性判断设备是否越狱;根据文件系统的分区是否发生变化来检查设备是否越狱;根据是否安装ssh来判断设备是否越狱)。用最近比较流行的一句话就是“你有一百种方式来检测越狱”,而别人也有一百种方式来抵抗你的检测。

0x06 魔高一尺道高一丈

刚才我们演示的是LLDB调试来绕过越狱检测,那么如果我的程序阻止调试器附加怎么办?我们来看下面的测试代码。

#include <sys/stat.h>

#include <stdio.h>

#include <sys/types.h>

#include <sys/sysctl.h>

#include <unistd.h>

inline int checkDebugger()__attribute__((always_inline));

int main(){

    if(checkDebugger()){

        printf("Debugger attached\n");

        return0;

    }

    printf("Debug detection bypassed\n");

    return0;

}


int checkDebugger(){

    int name[4];

    structkinfo_proc info;

    size_t info_size =sizeof(info);

    info.kp_proc.p_flag =0;

    name[0] =CTL_KERN;

    name[1] =KERN_PROC;

    name[2] =KERN_PROC_PID;

    name[3] =getpid();

    if(sysctl(name,4,&info,&info_size,NULL,0) == -1){

        return1;

    }

    return(info.kp_proc.p_flag &P_TRACED) ? 1 :0;

}


当一个应用被调试的时候,会给进程设置一个标识(P_TRACED),可以检测该进程是否有设置这个标识来检测进程是否正在被调试来保护应用。如果该程序发现自己被调试器附加了进程,那么将会输出Debugger attached,如果正常运行该程序则会输出Debug detection bypassed。实验结果如下:

图16 正常运行的输出结果

iOS APP 逆向安全杂谈之三_第11张图片

图17 使用LLDB调试器附加后的输出结果

在实际应用中可以在得知自己的程序被调试器附加后自动退出,当然这也是可以通过下断点的方式绕过,但是可以通过多处调用该方法来增加攻击的难度。同时上面的代码采用声明内联函数的方法,使编译器将函数功能插到每处代码被调用的地方,而不至于某个特定的功能函数被劫持。

当然除了上述的这种反调试的方法,还可以通过优化标记、去除符号等方法使反汇编复杂化。

0x07 冤冤相报何时了

0x06中所说的反调试还可以通过打补丁的方式绕过,这样就不需要每次都下断点绕过,也会有更多的时间用LLDB来调试其他核心业务。是不是感觉有些乱?因为的确是攻击和防御的方法都有很多,厂商采用多种防御手段来增加攻击成本,而攻击者为了利益或者抱有挑战心理的态度来见招拆招,真是冤冤相报啊。

参考文献:

安全链接:http://wiki.wooyun.org/

  1. [美]Jonathan Zdziarski《Hacking and Securing ios Application》
  2. 沙梓社,吴航《ios应用逆向工程》
  3. http://blog.ioactive.com/2015/09/the-ios-get-out-of-jail-free-card.html

你可能感兴趣的:(iOS APP 逆向安全杂谈之三)