时时勤拂拭,勿使惹尘埃

TOC

Categories

iOS(十六)一次通过lldb绕过越狱检测&反反调试实践


0x0 背景

最近在测试一个app时,发现启动app时会进行越狱检测,检测到越狱后会直接退出app,这样常规的越狱绕过工具如needle都是需要等程序运行后再attach,对此场景不太适用,所以这里需要使用lldb来进行绕过。

0x1 越狱检测

0x11 定位检测点

使用ida静态分析app的越狱检测方式,首先尝试通过检索cydiasubstratejailbreak等字符串尝试定位检测位置,但未检索到。从程序运行时间段来看,程序是启动时就会检测越狱并直接退出,故从app启动流程main()函数开始分析(iOS app启动流程可以参考:iOS(七)iOS程序的启动过程,另外main()函数在ida里自动解析为start()函数),在main()函数入口即进行越狱检测(手动改的名):

0x12 检测方式分析

app会进行越狱检测和重打包检测,检测到后立即退出

检测方式也比较简单,都是检查是否存在越狱相关文件,但文件路径都是拼接而成,所以开始通过相关字符串并未检索到:

0x13 绕过方式

绕过方式也比较简单,首先确定退出函数exit()的调用点,目前只有is_jailbreak_exit()中调用 (jailbreak_exit()只检测不退出):

查看is_jailbreak_exit()汇编代码,确定调用exit()的位置,这里使用CBNZ跳转,即当R0不为0时跳转到exit(),所以只需要在这里下断点,修改R0返回值为0即可绕过检测:

0x2 反调试

0x21 反调试方式

mani()函数中除了越狱检测,还有进行了反调试,这里检测方式为:
syscall(26,31,0,0,0);

syscall是系统提供的系统调用函数,实现从用户态切换到内核态,这里的调用号26实际为ptrace,可以在Kernel Syscalls查看:
26. ptrace               801e812c T
ptrace()是从Unix的早期版本开始就提供的一种对运行中的进程进行跟踪和控制的调试工具,通过ptrace可以对另一个进程实现调试跟踪。同时ptrace()还提供了一个非常有用的参数那就是PT_DENY_ATTACH,定义如下(参考自Bugging Debuggers),31即其调用号,这个参数用来告诉系统阻止调试器attach,所以这也是最常用的反调试方案:
#import <dlfcn.h>
#import <sys/types.h>

typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);
#if !defined(PT_DENY_ATTACH)
#define PT_DENY_ATTACH 31
#endif  // !defined(PT_DENY_ATTACH)

void disable_gdb() {
  void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
  ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
  ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
  dlclose(handle);
}
所以这里的syscall(26,31,0,0,0);相当于如下调用:
ptrace(PT_DENY_ATTACH, 0, 0, 0);

0x22 反反调试

绕过ptrace(PT_DENY_ATTACH, 0, 0, 0);方法也很简单,只需要修改PT_DENY_ATTACH调用号31为其他值即可,所以只需在执行_syscall时下断点,修改R1参数为其他值如0即可(0x1F=31):

0x3 lldb实践

0x31 iOS端启动调试进程

由于App运行检测到越狱后会直接退出,所以需要通过debugserver来启动程序:
#进入程序所在文件夹
$ cd /var/mobile/Containers/Bundle/Application/05FC912B-656F-44C9-A82E-58E1BE0DAFC2/*.app
#通过`debugserver`来启动程序
$ debugserver -x backboard 0.0.0.0:1234 ./*

0x32 macOS端通过lldb调试

  1. lldb远程attach:
    macOS上启动lldb后,需要使用process connect connect://<ip>:<port>来远程attach调试进程:
  2. 从上图可知,此时程序还在dyld内部,被调试程序尚未加载,需要通过ni单步执行到程序加载(ni执行下一条指令但不进入函数体,si执行下一条指令会进入函数体)
  3. ni执行到开始加载被调试程序,执行image list -o -f列举当前所有模块,获取被调试程序ASLR偏移地址0x00061000
  4. 获取ASLR偏移地址后,给上文分析的越狱检测和反调试位置下断点,再修改相应寄存器参数即可:
    #越狱检测绕过断点
    (lldb) br s -a '0x00061000+0x0006750C'
    #反调试调用断点
    (lldb) br s -a '0x00061000+0x00067550'
    #c指令运行程序,断在第一个断点越狱检测位置
    (lldb) c
    ...
    ->  0xc850c <+8>:  cbnz   r0, 0xc8516               ; <+18>
    ...
    #查看越狱检测跳转到exit时判断语句寄存器r0的值,当r0为0不跳转
    (lldb) p $r0
    (unsigned int) $2 = 1
    #r0=1,修改r0=0即可绕过越狱检测
    (lldb) register write r0 0
    #c指令运行程序,断在第二个断点反调试位置
    (lldb) c
    ...
    ->  0xc8550 <+52>: blx    0x5a3f84                  ; symbol stub for: syscall
    ...
    #查看syscall第二个参数即寄存器r1的值,31即为PT_DENY_ATTACH
    (lldb) p $r1
    (unsigned int) $3 = 31
    #修改r1为其他值如0,即可绕过反调试
    (lldb) register write r1 0
    #继续执行,已绕过反调试
    (lldb) c
    

0 评论:

发表评论