AntiAntiDebug助手

之前有写过针对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来绕过反调试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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🐯🐯🐯-------------");
}

反调试及其绕过的相关知识我在动态绕过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结构有一定了解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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);
}
}
}

总结

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

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

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

有钱的捧个钱场
0%