首页
论坛
专栏
课程

[原创]iOS LLDB中反反调试分析与实现

6天前 794

[原创]iOS LLDB中反反调试分析与实现

6天前
794

开始

关于反调试和反反调试,已经有很多人分析过了,也有很多解决方案。但是在LLDB中做反反调试还没人做过,这也是我一直想解决的一个方案,毕竟本身就是为了调试,那么在LLDB直接输入一行命令就能反反调试应该相对酸爽。本文将介绍一种基于内存单指令patch的方式进行反反调试的方案,大概意思就是通过直接修改代码段的指令来绕过反调试机制。

目前反调试与反反调试情况

这里庆哥写了一遍文章分析了 关于反调试&反反调试那些事

 

这里简单归纳一下,有如下几种:

  • ptrace
  • sysctl
  • syscall
  • SIGTOP
  • task_get_exception_ports

这里实际上就大概三种,其他都是基于ptrace的变种。ptrace这个函数是linux就提供的一个接口,常常用作linux系的反调试,本质就是通过26号系统调用来完成的,目前大多反调试都利用该方案。

 

后面看到庆哥同样提供了一个反反调试的LLDB脚本,不过看了下和我的思路还是不一样的。(

 

正如文章里面写到通过lldb下断点,然后修改参数,或者直接返回也可以达到反反调试的效果。由于要不断去检查执行状态等,或者程序有定时器定时检测,这个脚本影响性能及变得很卡影响调试体验。不过还是膜庆哥的方案,学习了。

内存patch实现反反调试

说一下大概思路:

  • 内存中找到ptrace地址
  • 将该内存map为rwx
  • 直接将首调指令修改为ret指令

    刚开始以为就这样简单就完了,结果实际写代码的时候才发现过程远比想象中复杂。

由于iOS不允许直接将代码段map为写权限,这里调用mach_vm_protectmprotect都会异常。但是类似frida、substitute以及hookzz都能进行指令hook。这样说来,肯定是可以修改代码段的。看了下substitute以及frida中关于这块的实现,才发现可以用一种remap的方式修改代码段。

 

大致的流程如下:

  • 使用mmap新建一块内存,把这块内存叫做new
  • 使用vm_copy把想要篡改的处于__text段内的内存(把这块内存叫target)拷贝到new里
  • 向new里写入想执行的代码
  • 调用mprotect把new改为rx。因为mmap出来的内存的max_protection是rwx,所以这里mprotect改权限没问题
  • 调用mach_vm_remap把new的内容反映回target里

不过当我写代码测试的时候发现,remap以后整个页数据都变成了0。实在不清楚原因,向Zz求助,Zz直接扔了我他实现这块的代码。我看了以后收益匪浅,只怪之前没分析hookzz的具体实现。后面才知道由于我的设备是iOS12,Zz意思是codesign的问题,hookZz也没支持。于是换了一台iOS9的设备,果然就可以了,向Zz低头。

 

期间还由于我手残忘记调用mprotect把new改为rx。导致直接执行异常,用memory region查看地址才知道页保护属性为rw

 

相关代码如下:

 

1、map new page for patch

    // map new page for patch
    void *new = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
    if (!new ){
        NSLog(@"[-] mmap failed!");
        return;
    }
    NSLog(@"[*] new map address:%p", new);

2、start patch

    // start patch
        kret = vm_copy(self_task, (unsigned long)page_start, 0x1000, (vm_address_t) new);
    if (kret != KERN_SUCCESS){
        NSLog(@"[-] kr: %d, errno: %d", kret, errno);
        return;
    }

    char patch_ret_ins_data[4] = {0xc0, 0x03, 0x5f, 0xd6}; // ret 
    memcpy((void *)(new+patch_offset), patch_ret_ins_data, 4);

    NSLog(@"[*] new map+offset address:%p", (void *)(new+patch_offset));

3、set new page back to r-x

    // set back to r-x
    int ret = mprotect(new, 0x1000, PROT_READ | PROT_EXEC);
    NSLog(@"[*] ret: %d, errno: %d, addr: %p", ret, errno, new);

4、remap the target page

    kret = mach_vm_remap(mach_task_self(), &target, 0x1000, 0,
                       VM_FLAGS_OVERWRITE, self_task,
                       (mach_vm_address_t) new, TRUE,
                       &c, &m, inherit);

    if(kret != KERN_SUCCESS){
        NSLog(@"[-] kr: %d, errno: %d", kret, errno);
        return;
    }

    NSLog(@"[*] now ptrace_ptr address:%p", ptrace_ptr)

5、clear cache

    void* clear_start_ = (void*)page_start + patch_offset;
  sys_icache_invalidate (clear_start_, 4);
  sys_dcache_flush (clear_start_, 4);

完整的代码在xia0LLDB里面已经集成:https://github.com/4ch12dy/xia0LLDB

一个简单反反调试实验

这里以爱奇艺为例子分析,爱奇艺在main函数里面动态调用了ptrace函数进行反调试。

  • 后台启动方式启动爱奇艺

    xia0 ~ $ issh debug -x backboard /var/containers/Bundle/Application/F9D8AACA-30F0-4F26-96CA-5B06782CC903/iQiYiPhoneVideo.app/iQiYiPhoneVideo
    [I]:iproxy process for 2222 port alive, pid=16264 
    [I]:++++++++++++++++++ Nice to Work :) +++++++++++++++++++++ 
    [I]:iOSRE dir exist 
    [I]:iproxy process for 1234 port alive, pid=16428 
    [I]:Run ps -e | grep debugserver | grep -v grep; [[ 0 == 0 ]] && (killall -9 debugserver 2> /dev/null) 
    [I]:/iOSRE/tools/debugserver file exist, Start debug... 
    [I]:Run /iOSRE/tools/debugserver 127.0.0.1:1234 -x backboard /var/containers/Bundle/Application/F9D8AACA-30F0-4F26-96CA-5B06782CC903/iQiYiPhoneVideo.app/iQiYiPhoneVideo
    
  • LLDB挂上以后在main函数下断点以后直接执行debugme命令

    (lldb) debugme
    Kill antiDebug by xia0:
    [*] target address: 6501024128 and offset: 384
    [*] mmap new page: 4572217344 success! 
    [+] vm_copy success!
    [+] mach_vm_write success!
    [*] set new page back to r-x success!
    [*] vm_region_recurse_64 success!
    [*] get page info success!
    [+] remap success!
    [*] clear cache success!
    [+] all done! happy debug~
    

    下面查看对比下patch前后指令ptrace首指令的变化

    Patch之前

    (lldb) x/12i 0x00000001837dc180
        0x1837dc180: 0xf00f26a9   adrp   x9, 124119
        0x1837dc184: 0x91034129   add    x9, x9, #0xd0             ; =0xd0 
        0x1837dc188: 0xb900013f   str    wzr, [x9]
        0x1837dc18c: 0xd2800350   mov    x16, #0x1a
        0x1837dc190: 0xd4001001   svc    #0x80
        0x1837dc194: 0x540000c3   b.lo   0x1837dc1ac               ; <+44>
        0x1837dc198: 0xa9bf7bfd   stp    x29, x30, [sp, #-0x10]!
        0x1837dc19c: 0x910003fd   mov    x29, sp
        0x1837dc1a0: 0x97ff9b08   bl     0x1837c2dc0               ; cerror
        0x1837dc1a4: 0x910003bf   mov    sp, x29
        0x1837dc1a8: 0xa8c17bfd   ldp    x29, x30, [sp], #0x10
        0x1837dc1ac: 0xd65f03c0   ret
    

    Patch之后

    (lldb) x/12i 0x1837dc180
        0x1837dc180: 0xd65f03c0   ret    
        0x1837dc184: 0x91034129   add    x9, x9, #0xd0             ; =0xd0 
        0x1837dc188: 0xb900013f   str    wzr, [x9]
        0x1837dc18c: 0xd2800350   mov    x16, #0x1a
        0x1837dc190: 0xd4001001   svc    #0x80
        0x1837dc194: 0x540000c3   b.lo   0x1837dc1ac               ; <+44>
        0x1837dc198: 0xa9bf7bfd   stp    x29, x30, [sp, #-0x10]!
        0x1837dc19c: 0x910003fd   mov    x29, sp
        0x1837dc1a0: 0x97ff9b08   bl     0x1837c2dc0               ; cerror
        0x1837dc1a4: 0x910003bf   mov    sp, x29
        0x1837dc1a8: 0xa8c17bfd   ldp    x29, x30, [sp], #0x10
        0x1837dc1ac: 0xd65f03c0   ret
    

    可以发现首地址已经变成了ret指令。

  • 执行continue命令,发现爱奇艺已经能够正常调试。

    (lldb) c
    Process 3176 resuming
    2019-08-13 17:22:17.283 iQiYiPhoneVideo[3176:161840] [plcrash]: init ok
    2019-08-13 17:22:17.790 iQiYiPhoneVideo[3176:161840] -[QYBaikePageDurationManager bk_appDidBecomeActive:]
    2019-08-13 17:22:17.922 iQiYiPhoneVideo[3176:161840] CoreData: Failed to load optimized model at path '/var/containers/Bundle/Application/F9D8AACA-30F0-4F26-96CA-5B06782CC903/iQiYiPhoneVideo.app/QYPGCDataModel.momd/QYPGCDataModel_970.omo'
    2019-08-13 17:22:20.477 iQiYiPhoneVideo[3176:161840] OSStatus error: [-34018] Security error has occurred.
    2019-08-13 17:22:20.558 iQiYiPhoneVideo[3176:162000] OSStatus error: [-34018] Security error has occurred.
    3176:161840] Incorrect NSStringEncoding value 0x8000100 detected. Assuming NSASCIIStringEncoding. Will stop this compatiblity mapping behavior in the near future.
    ontainers/Data/Application/5C31FE18-9BA4-4B2D-80C6-68BF7F65855F/Library/Application Support/爱奇艺/0_im.sqlite
    

总结/Todo

这里只是简单的绕过了ptrace方式的反调试,针对直接用汇编写的反调试我的做法是静态内存搜索匹配svc位置,发现是调用26号系统调用则利用内存patch为nop。或者写一个简单的hook代码,hook所有的svc地址,判断寄存器的值然后进行hook即可,这样就能绕过这些反调试机制,再次向Zz和庆哥低头。

参考/致谢

  • https://gist.github.com/piaoger/4ba83f6a954bca113505

  • https://github.com/comex/substitute/blob/master/lib/darwin/execmem.c

  • https://www.codercto.com/a/63507.html

  • https://github.com/jmpews/HookZz/blob/dev/srcxx/UserMode/ExecMemory/code-patch-tool-darwin.cc#L26

  • https://github.com/gdbinit/readmem/blob/master/readmem/main.c

  • https://github.com/frida/frida-gum/blob/master/gum/backend-darwin/machexc.h

  • http://newosxbook.com/src.jl?tree=xnu&file=/libsyscall/mach/mach_vm.c

  • https://github.com/DaKnOb/memscan/blob/master/main.mm

  • http://iosre.com/t/topic/8179



[招聘]欢迎市场人员加入看雪学院团队!

最后于 6天前 被xia0编辑 ,原因:
最新回复 (13)
葫芦娃 1 6天前
2
0
“于是换了一台iOS9的设备”
向土豪低头
xia0 6天前
3
0

非要我放截图吗

最后于 5天前 被xia0编辑 ,原因:
4
0
xia0 非要我放截图吗
修改没有用 我已经看到了 大佬之间的互相伤害
最后于 5天前 被上海刘一刀编辑 ,原因:
eastmaster 5天前
5
0
Breathleas 5天前
6
0
看大佬撕逼
xia0 5天前
7
0
上海刘一刀 xia0 非要我放截图吗 修改没有用&nbsp;我已经看到了&nbsp;大佬之间的互相伤害
完了,暴露了葫芦娃上海第一豪的身份
8
0
task_get_exception_ports,这个方法不能用的,会误报。
xia0 5天前
9
0
暗夜盗魔 task_get_exception_ports,这个方法不能用的,会误报。
嗯,一般也没怎么用。iOS大多还是用ptrace
Nermor 1 4天前
10
0
Zz 有一套反反调试的代码, 基于hookzz, xia0LLDB 直接集成是不是更省事些?
当然 这从研究的角度来说 还是自己撸一遍原理来的透彻些,  给xiao0 点个赞
xia0 4天前
11
0
Nermor Zz 有一套反反调试的代码, 基于hookzz, xia0LLDB 直接集成是不是更省事些? 当然 这从研究的角度来说 还是自己撸一遍原理来的透彻些, 给xiao0 点个赞[em_63]
这里并没有重复造轮子,原因在于LLDB中的代码限制很大,不可能集成一个hookzz这样的框架。虽然hookzz已经算轻量级hook框架了,但在这里也显得太重了,而且也没必要。这里并不需要一个通用的hook框架。后面我对于SVC指令hook的思路也仅仅是判断寄存器简单操作,不然还会涉及污染寄存器等考虑因素。所以基于以上原因,我就自己实现了一遍,应该算最轻的一种hook方案。
最后于 4天前 被xia0编辑 ,原因:
Nermor 1 4天前
12
0
xia0 Nermor Zz 有一套反反调试的代码, 基于hookzz, xia0LLDB 直接集成是不是更省事些? 当然 这从研究的角度来说 还是自己撸一遍原理来的 ...
原来如此, 感谢大佬指点。
TinHsieh 3天前
13
0
Kill antiDebug by xia0:error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=1, subcode=0x0).
The process has been returned to the state before expression evaluation.
请问大佬这样的情况该如何排除呢?
xia0 3天前
14
0
TinHsieh Kill antiDebug by xia0:error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=1, subcod ...
设备和系统版本是多少
游客
登录 | 注册 方可回帖
返回