首页
社区
课程
招聘
[原创]iOS反反调试助手
发表于: 2021-6-29 11:55 84687

[原创]iOS反反调试助手

2021-6-29 11:55
84687

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

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

MochO文件结构

dyld链接加载流程

iOS inline Hook

Preferenceloader,applist模块集成

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

通过阅读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两个依赖

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

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

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

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

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

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

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

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

 
 
 
 
NSDictionary *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-------------");
    }
NSDictionary *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-------------");
    }
 
void 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 <= (zpointer)0)
        printf("search address start_addr(%p) < 0", (zpointer)start_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);
        }
    }
}
void 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;
        }
    }
}

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2021-6-29 11:55 被Night_elf编辑 ,原因:
收藏
免费 13
支持
分享
最新回复 (16)
雪    币: 42
活跃值: (286)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
大佬牛逼!
2021-6-29 12:02
0
雪    币: 2089
活跃值: (3933)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
支持
2021-6-29 17:39
0
雪    币: 3659
活跃值: (2107)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
支持
2021-6-30 11:37
0
雪    币: 2686
活跃值: (1020)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
5
有的app的代码段不在Section(__TEXT,__text),例如某app代码段为Section(__BD_TEXT,__text)
2021-7-19 18:26
0
雪    币: 13
活跃值: (49)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
App有越狱检测闪退,拖入ida找到svc 80段首ret 修复闪退,用大佬的测试只打印了svc地址 还是闪退 什么原因
2021-7-21 00:11
0
雪    币: 1818
活跃值: (1321)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
7
luoyanbei 有的app的代码段不在Section(__TEXT,__text),例如某app代码段为Section(__BD_TEXT,__text)
好的,之后有空可以把这个段也加进去,感谢提醒
2021-7-27 17:13
0
雪    币: 1818
活跃值: (1321)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
8
每天有艳遇 App有越狱检测闪退,拖入ida找到svc 80段首ret 修复闪退,用大佬的测试只打印了svc地址 还是闪退 什么原因
你绕过越狱检测了吗?
2021-7-27 17:14
0
雪    币: 13
活跃值: (49)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
检测越狱执行svc 80 不能直接hook掉吗 我修改16进制都可以
2021-8-1 18:58
0
雪    币: 1818
活跃值: (1321)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
10
每天有艳遇 检测越狱执行svc 80 不能直接hook掉吗 我修改16进制都可以
如果单纯是ptrace是可以的,涉及到syscall调用sysctl不行
2021-8-2 17:24
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
每天有艳遇 检测越狱执行svc 80 不能直接hook掉吗 我修改16进制都可以
咋使用的这个
2021-8-17 11:32
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
每天有艳遇 检测越狱执行svc 80 不能直接hook掉吗 我修改16进制都可以
请问这个咋使用的
2021-8-17 11:33
0
雪    币: 4
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
最新版的抖音应该不行吧
2021-8-20 14:12
0
雪    币: 1233
活跃值: (270)
能力值: ( LV3,RANK:29 )
在线值:
发帖
回帖
粉丝
14

请问这个怎么解

2021-8-26 01:32
0
雪    币: 17
活跃值: (75)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
你好,请问你这个脚本用Frida能写得出来吗?
2023-2-15 16:36
0
雪    币: 520
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
方便发个成品嘛
2023-2-20 16:37
0
游客
登录 | 注册 方可回帖
返回
//