[原创]iOS反反调试助手 您所在的位置:网站首页 dobby框架分析 [原创]iOS反反调试助手

[原创]iOS反反调试助手

2023-11-15 12:21| 来源: 网络整理| 查看: 265

之前有写过针对iOS反调试的及其绕过的分析文章,动态绕过iOS内联svc反调试,本篇主要分享一下自己集成的一款方便绕过反调试的工具。

前言反调试相关技术参考

反调试与绕过的奇淫技巧关于反调试&反反调试那些事

相关知识储备

MochO文件结构

dyld链接加载流程

iOS inline Hook

Preferenceloader,applist模块集成

 

之前对反调试的绕过一般都是采用针对对应APP编写Hook插件来绕过反调试,或者是启动调试定位反调试点,修改寄存器nop汇编指令进行绕过,前几天在使用RevealLoader的过程中,看到其下图界面,突发奇想是否可以开发一个通用的插件,选中某个APP即可绕过其反调试,不再需要逐个分析APP的反调试的反调试点,于是通过之前对反调试的一些积累集成了本工具。

 

集成AppList 和 PreferenceLoader

通过阅读RevealLoader源码,发现想要实现在设置中添加每个APP的开关选项,需要依赖PreferenceLoader和AppList两个模块。

 

PreferenceLoader 是一个 MobileSubstrate 提供的模块,它可以让开发者在系统设置界面添加应用程序入口。 AppList 是一个让开发者获取系统中已安装应用信息的库。这两个模块相结合即可实现RevealLoader中呈现的效果。

 

插件开发使用MonKeyDev,新建Logos Tweak项目,在/Package/Library目录下新建PreferenceLoader/Preferences文件夹,文件夹中存放ProjectName.plist配置文件以及插件所需图标图片文件。下图为ProjectName.plist文件的详细内容, ALSettingsPath指定插件设置对应的存储路径,ALSettingsKeyPrefix则是每个应用配置的前缀。在control中的Depends字段添加AppList和PreferenceLoader两个依赖

Tweak编写

代码中首先需要读取前面plist文件中保存的配置信息,通过前缀连接APP的bundleid来判断对应APP是否在设置中打开开关,如果打开,就进行下面的AntiDebug_Hook来绕过反调试。

1234567891011121314151617NSDictionary *pref = [NSDictionary dictionaryWithContentsOfFile:@"/var/mobile/Library/Preferences/com.yunnigu.AntiAntiDebug.plist"];    NSString *keyPath = [NSString stringWithFormat:@"AntiAntiDebugEnabled-%@", [[NSBundle mainBundle] bundleIdentifier]];    if ([[pref objectForKey:keyPath] boolValue]) {        NSLog(@"-------AntiAntiDebug----------Start-------------");        //ptrace        MSHookFunction((void *)MSFindSymbol(NULL,"_ptrace"),(void*)my_ptrace,(void**)&orig_ptrace);        //dlsym        MSHookFunction((void *)dlsym,(void*)my_dlsym (void**)&orig_dlsym);        //sysctl        MSHookFunction((void *)sysctl,(void*)my_sysctl,(void**)&orig_sysctl);        //syscall        MSHookFunction((void *)syscall,(void*)my_syscall,(void**)&orig_syscall);        //svc 0x80        hook_svc_x80();        NSLog(@"[AntiAntiDebug] Module loaded!!!");        NSLog(@"-------AntiAntiDebug----------End-------------");    }

反调试及其绕过的相关知识我在动态绕过iOS内联svc反调试中已经有过分享,本篇主要详细分析一下对svc 0x80进行Hook的流程。

svc 0x80 Hook

这种方式主要是通过内嵌汇编的方式对ptrace等反调试函数进行调用,之前对其绕过的处理方式一般都是讲该段汇编代码以NOP进行代替,这种方式在定位到反调试调用地址的前提下很好用,在本工具中由于无法定位反调试地址,因此我们只能在text段中搜索svc 0x80指令,APP有时在正常逻辑中同样使用svc 0x80进行调用,如果统一NOP可能会造成崩溃的情况出现,因此我们需要在找到svc 0x80指令之后,对其进行Hook判断其传参,对反调试做出应对。

 

以下是hook_svc0x80的详细代码,代码Hook基于Hookzz目前升级为Dobby框架,以代码注释进行说明,想要深入理解svc 0x80指令获取流程,需要对MachO结构有一定了解。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130void hook_svc_x80() {    zaddr svc_x80_addr;    zaddr curr_addr, text_start_addr, text_end_addr;    uint32_t svc_x80_byte = 0xd4001001;    //首先通过dyld中的_dyld_get_image_header获取主程序的macho_header    const struct mach_header *header = _dyld_get_image_header(0);    //获取__TEXT segment结构体    struct segment_command_64 *seg_cmd_64 = zz_macho_get_segment_64_via_name((struct mach_header_64 *)header, (char *)"__TEXT");    //计算Macho内存中基地址到__TEXT偏移    zsize slide = (zaddr)header - (zaddr)seg_cmd_64->vmaddr;    //获取__text section结构体    struct section_64 *sect_64 = zz_macho_get_section_64_via_name((struct mach_header_64 *)header, (char *)"__text");    //计算出__text实际地址    text_start_addr = slide + (zaddr)sect_64->addr;    text_end_addr = text_start_addr + sect_64->size;    curr_addr = text_start_addr;    //循环查找svc 0x80指令,进行Hook操作    while (curr_addr < text_end_addr) {        svc_x80_addr = (zaddr)zz_vm_search_data((zpointer)curr_addr, (zpointer)text_end_addr, (zbyte *)&svc_x80_byte, 4);        if (svc_x80_addr) {            NSLog(@"hook svc #0x80 at %p with aslr (%p without aslr)",                  (void *)svc_x80_addr, (void *)(svc_x80_addr - slide));            ZzBuildHookAddress((void *)svc_x80_addr, (void *)(svc_x80_addr + 4),                               hook_svc_pre_call, hook_svc_half_call, TRUE);            ZzEnableHook((void *)svc_x80_addr);            curr_addr = svc_x80_addr + 4;        } else {            break;        }    }}  struct section_64 *zz_macho_get_section_64_via_name(sstruct segment_command_64 *seg_cmd_64,                                 char *sect_name) {    struct section_64 *sect_64;     sect_64 = (struct section_64 *)((zaddr)seg_cmd_64 + sizeof(struct segment_command_64));    //在__TEXT中循环查找section,返回__text section    for (zsize j = 0; j < seg_cmd_64->nsects;         j++, sect_64 = (struct section_64 *)((zaddr)sect_64 + sizeof(struct section_64))) {        if (!strcmp(sect_64->sectname, sect_name)) {            return sect_64;        }    }}  zpointer zz_vm_search_data(const zpointer start_addr, zpointer end_addr, zbyte *data,                           zsize data_len){    zpointer curr_addr;    if (start_addr end_addr)        printf("search start_add(%p) < end_addr(%p)", (zpointer)start_addr, (zpointer)end_addr);     curr_addr = start_addr;     while (end_addr > curr_addr)    {        if (!memcmp(curr_addr, data, data_len))        {            return curr_addr;        }        curr_addr = (zpointer)((zaddr)curr_addr + data_len);    }    return 0;} struct segment_command_64 *zz_macho_get_segment_64_via_name(struct mach_header_64 *header,                                 char *segment_name) {    struct load_command *load_cmd;    struct segment_command_64 *seg_cmd_64;    struct section_64 *sect_64;    //确定load_cmd地址    load_cmd = (struct load_command *)((zaddr)header + sizeof(struct mach_header_64));    //循环load_cmd中的segment,返回__TEXT segment    for (zsize i = 0; i < header->ncmds;         i++, load_cmd = (struct load_command *)((zaddr)load_cmd + load_cmd->cmdsize)) {        if (load_cmd->cmd == LC_SEGMENT_64) {            seg_cmd_64 = (struct segment_command_64 *)load_cmd;            if(!strcmp(seg_cmd_64->segname, segment_name)) {                return seg_cmd_64;            }        }    }    return NULL;}  void hook_svc_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {    int num_syscall;    int request;    num_syscall = (int)(uint64_t)(rs->general.regs.x16);    request = (int)(uint64_t)(rs->general.regs.x0);     if (num_syscall == SYS_syscall) {        int arg1 = (int)(uint64_t)(rs->general.regs.x1);        if (request == SYS_ptrace && arg1 == PT_DENY_ATTACH) {            *(unsigned long *)(&rs->general.regs.x1) = 0;            NSLog(@"[AntiAntiDebug] catch 'SVC #0x80; syscall(ptrace)' and bypass");        }     } else if (num_syscall == SYS_ptrace) {        request = (int)(uint64_t)(rs->general.regs.x0);        if (request == PT_DENY_ATTACH) {            *(unsigned long *)(&rs->general.regs.x0) = 0;            NSLog(@"[AntiAntiDebug] catch 'SVC-0x80; ptrace' and bypass");        }    } else if(num_syscall == SYS_sysctl) {        STACK_SET(callstack, (char *)"num_syscall", num_syscall, int);        STACK_SET(callstack, (char *)"info_ptr", rs->general.regs.x2, zpointer);    }} void hook_svc_half_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {     if(STACK_CHECK_KEY(callstack, (char *)"num_syscall")) {        int num_syscall = STACK_GET(callstack, (char *)"num_syscall", int);        struct kinfo_proc *info = STACK_GET(callstack, (char *)"info_ptr", struct kinfo_proc *);        if (num_syscall == SYS_sysctl)        {            NSLog(@"[AntiAntiDebug] catch 'SVC-0x80; sysctl' and bypass");            info->kp_proc.p_flag &= ~(P_TRACED);        }    }} 总结

目前测试多款APP均绕过其反调试检测,各位看官在使用过程中如遇到无法绕过的情况,请在评论区留言,先提前感谢大家帮我完善工具。

 

在测试一部iOS12.1.1中遇到在勾选对应APP之后,调试显示debugserver无法找到程序进程,在启动时进行挂载即可解决该问题,具体问题出现还在定位当中。

 

项目的github地址:https://github.com/yunnigu/AntiAntiDebug

本文仅用来学习交流,违法犯罪禁止使用,否则后果自付!如有侵权,请联系作者删除,本项目具体使用指南请参照网络安全法

[培训]《安卓高级研修班(网课)》月薪三万计划

最后于 2021-6-29 11:55 被Night_elf编辑 ,原因: #逆向分析 #工具脚本


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有