iOS 反调试
0x01
反调试主要分为两种,第一种阻止调试器附加,第二种是检测是否有调试器存在
0x02
第一种方法:
0x01 ptrace
ptrace是系统用来对运行中的进程进行调试和跟踪的工具,通过ptrace,可以对另一个进程实现调试跟踪。但是里面提供了一个非常有用的参数,就是PT_DENY_ATTACH,const值是31,这个参数用户告诉系统阻止调试器附加。
在main.m里面加入以下代码:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <dlfcn.h>
#import <sys/types.h>
typedef int (*ptrace_ptr_t)(int _request,pid_t pid,caddr_t _addr,int _data);
#ifndef PT_DENY_ATTACH
#define PT_DENY_ATTACH 31
#endif
int main(int argc, char * argv[]) {
@autoreleasepool {
// ptrace(PT_DENY_ATTACH,0,0,0); //系统函数并没有暴露出此方法所以不能直接通过此方式调用
void *handle = dlopen(0, RTLD_NOW|RTLD_GLOBAL);
ptrace_ptr_t ptrace_ptr = (ptrace_ptr_t)dlsym(handle, "ptrace");
ptrace_ptr(PT_DENY_ATTACH,0,0,0);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
在上面的代码中,本来是直接调用上面被注释的那一行代码就可以了,不过由于不是公开的函数所以没法直接调用。所以我们通过dlopen的方式,当path 参数为0是,他会自动查找 LD_LIBRARY_PATH,DYLD_LIBRARY_PATH, $DYLD_FALLBACK_LIBRARY_PATH 和 当前工作目录中的动态链接库,通过句柄找到对应的ptarce对应的地址,然后传入PT_DENY_ATTACH。
0x02 syscall
另外一种方式可以使用syscall的方式来调用ptrace,syscall是系统提供的一个系统调用函数,因为上面的调用方式会容易被反反调试,通过NSFindSymbol找到_ptrace然后hook对应的函数,所以可以最好是通过syscall来反调试
在Kernel Syscalls里面找到ptrace对应的const。
$ joker -u ~/Documents/projects/iOS.6.0.iPod4.kernel
This is an ARM binary. Applying iOS kernel signatures
Entry point is 0x80085084....This appears to be XNU 2107.2.33
Syscall names are @2a70f0
Sysent offset in file/memory (for patching purposes): 0x2ef0c0/0x802f00c0
Suppressing enosys (0x800b3429) T = Thumb
1. exit 801d4a74 T
2. fork 801d7980 T
3. read 801eb584 T
4. write 801eb958 T
5. open 800b13a4 T
6. close 801ccab4 T
7. wait4 801d56bc T
9. link 800b18e8 T
10. unlink 800b1ff0 T
12. chdir 800b0c60 T
13. fchdir 800b0af0 T
14. mknod 800b14bc T
15. chmod 800b2b40 T
16. chown 800b2c9c T
18. getfsstat 800b088c T
20. getpid 801dc20c T
23. setuid 801dc4c0 T
24. getuid 801dc290 T
25. geteuid 801dc2a0 T
26. ptrace 801e812c T
27. recvmsg 8020a8fc T
28. sendmsg 8020a444 T
29. recvfrom 8020a528 T
30. accept 80209dfc T
31. getpeername 8020abc8 T
32. getsockname 8020ab18 T
33. access 800b24ac T
34. chflags 800b2928 T
35. fchflags 800b29f0 T
36. sync 800b0320 T
37. kill 801dfdcc T
39. getppid 801dc214 T
41. dup 801cab04 T
42. pipe 801edbe4 T
43. getegid 801dc318 T
46. sigaction 801deee8 T
47. getgid 801dc308 T
48. sigprocmask 801df42c T
49. getlogin 801dd0e8 T
50. setlogin 801dd160 T
51. acct 801c54ec T
52. sigpending 801df5d0 T
注意一下代码中的26就是ptrace的const。
综上所述:调用syscall(26,31,0,0,0)就可以达到反调试的目的。
0x03 sysctl
可以通过sysctl查看内核进程状态标志位,如果一个进程在调试状态,会有一个标志位(info.kp_proc.p_flag)来标识当前是否正在调试。
代码如下:
BOOL existDebugger(){
int name[4];//指定查询信息的数组
struct kinfo_proc info;//查询的返回结果
size_t info_size = sizeof(info);
info.kp_proc.p_flag = 0;
name[0] = CTL_KERN;
name[1] = KERN_PROC;
name[2] = KERN_PROC_PID;
name[3] = getpid();
if (sysctl(name, 4, &info, &info_size, NULL, 0) == -1) {
NSLog(@"sysctl error ...");
return NO;
}
return ((info.kp_proc.p_flag & P_TRACED) != 0);
}
可以定时执行以上代码,当检测到程序正在被调试,可以调用exit(0)来让程序奔溃或者做其他的操作
0x04
syscall可以通过软中断实现从用户态切换到系统内核态的转换,同时可以通过arm 汇编实现以上功能。通过asm volatile内联汇编,实际上也是调用了ptrace。
代码如下:
#ifdef __arm__
asm volatile(
"mov r0,#31\n"
"mov r1,#0\n"
"mov r2,#0\n"
"mov r12,#26\n"
"svc #80\n"
);
#endif
#ifdef __arm64__
asm volatile(
"mov x0,#26\n"
"mov x1,#31\n"
"mov x2,#0\n"
"mov x3,#0\n"
"mov x16,#0\n"
"svc #128\n"
);
#endif