1. 禁止附加进程Ptrace
ptrace是在iOS项目中不能直接使用,需要去命令行项目下拷贝出头文件,才能使用,#import <sys/ptrace.h>
后进去拷贝
#ifndef _SYS_PTRACE_H_
#define _SYS_PTRACE_H_
#include <sys/appleapiopts.h>
#include <sys/cdefs.h>
enum {
ePtAttachDeprecated __deprecated_enum_msg("PT_ATTACH is deprecated. See PT_ATTACHEXC") = 10
};
#define PT_TRACE_ME 0 /* child declares it's being traced */
#define PT_READ_I 1 /* read word in child's I space */
#define PT_READ_D 2 /* read word in child's D space */
#define PT_READ_U 3 /* read word in child's user structure */
#define PT_WRITE_I 4 /* write word in child's I space */
#define PT_WRITE_D 5 /* write word in child's D space */
#define PT_WRITE_U 6 /* write word in child's user structure */
#define PT_CONTINUE 7 /* continue the child */
#define PT_KILL 8 /* kill the child process */
#define PT_STEP 9 /* single step the child */
#define PT_ATTACH ePtAttachDeprecated /* trace some running process */
#define PT_DETACH 11 /* stop tracing a process */
#define PT_SIGEXC 12 /* signals as exceptions for current_proc */
#define PT_THUPDATE 13 /* signal for thread# */
#define PT_ATTACHEXC 14 /* attach to running process with signal exception */
#define PT_FORCEQUOTA 30 /* Enforce quota for root */
#define PT_DENY_ATTACH 31
#define PT_FIRSTMACH 32 /* for machine-specific requests */
__BEGIN_DECLS
//_request是要做的事情,_pid是进程id,_addr,_data取决于第一个参数
int ptrace(int _request, pid_t _pid, caddr_t _addr, int _data);
__END_DECLS
#endif /* !_SYS_PTRACE_H_ */
1.1 ptrace禁止附加进程
ptrace(PT_DENY_ATTACH, 0, 0, 0);
ptrace 防护的一个特点:
1、重签名(Xcode)运行之后闪退!
2、手动打开正常运行!
1.2 反ptrace
道理很简单,就是利用fishhook重绑定
#import "fishhook.h"
#import "MyPtraceHeader.h"
@implementation insertDylib
//定义指针,保存原来的函数地址
int (*ptrace_p)(int _request, pid_t _pid, caddr_t _addr, int _data);
//自己的的ptrace
int my_ptrace(int _request, pid_t _pid, caddr_t _addr, int _data){
if (_request != PT_DENY_ATTACH) {//如果不是拒绝附加
return ptrace_p(_request,_pid,_addr,_data);
}
//如果是拒绝附加,直接return
return 0;
}
+(void)load
{
struct rebinding ptraceBd;
ptraceBd.name = "ptrace";
ptraceBd.replacement = my_ptrace;
ptraceBd.replaced = (void *)&ptrace_p;
struct rebinding binds[] = {ptraceBd};
rebind_symbols(binds, 1);
}
2. 反调试sysctl
2.1 sysctl可以查询是否是debug状态,代码如下:
BOOL isDebug(){
int name[4];//里面放字节码。查询的信息
name[0] = CTL_KERN;//内核查询
name[1] = KERN_PROC;//查询进程
name[2] = KERN_PROC_PID;//传递的参数是进程的ID
name[3] = getpid();//PID的值
struct kinfo_proc info;//接受查询结果的结构体
size_t info_size = sizeof(info);
if(sysctl(name, 4, &info, &info_size, 0, 0)){
NSLog(@"查询失败");
return NO;
}
//看info.kp_proc.p_flag 的第12位。如果为1,表示调试状态。
//(info.kp_proc.p_flag & P_TRACED)
return ((info.kp_proc.p_flag & P_TRACED) != 0);
}
2.2 反sysctl
#import "fishhook.h"
#import <sys/sysctl.h>
@implementation InjectCode
//原始函数指针
int (*sysctl_p)(int *, u_int, void *, size_t *, void *, size_t);
//新函数地址
int my_sysctl(int *name, u_int namelen, void *info, size_t *infosize, void *newInfo, size_t newInfoSize){
if (namelen == 4
&& name[0] == CTL_KERN
&& name[1] == KERN_PROC
&& name[2] == KERN_PROC_PID
&& info
&& (int)*infosize == sizeof(struct kinfo_proc)) {
int err = sysctl_p(name,namelen,info,infosize,newInfo,newInfoSize);
struct kinfo_proc * myinfo = (struct kinfo_proc *)info;
if ((myinfo->kp_proc.p_flag & P_TRACED) != 0) {
//使用异或可以取反
myinfo->kp_proc.p_flag ^= P_TRACED;
}
return err;
}
return sysctl_p(name,namelen,info,infosize,newInfo,newInfoSize);
}
+(void)load
{
rebind_symbols((struct rebinding[1]){{"sysctl",my_sysctl,(void *)&sysctl_p}}, 1);
}
3. 反fishhook
通过dlopen查找ptrace真正的函数地址
#import "MyPtraceHeader.h"
#import <dlfcn.h>
- (void)viewDidLoad {
[super viewDidLoad];
//拼接一个 ptrace
unsigned char funcStr[] = {
('a' ^ 'p'),
('a' ^ 't'),
('a' ^ 'r'),
('a' ^ 'a'),
('a' ^ 'c'),
('a' ^ 'e'),
('a' ^ '\0'),
};
unsigned char * p = funcStr;
while (((*p) ^= 'a') != '\0') p++;
//通过dlopen拿到句柄
void * handle = dlopen("/usr/lib/system/libsystem_kernel.dylib", RTLD_LAZY);
//定义函数指针
int (*ptrace_p)(int _request, pid_t _pid, caddr_t _addr, int _data);
if (handle) {
ptrace_p = dlsym(handle, (const char *)funcStr);
if (ptrace_p) {
ptrace_p(PT_DENY_ATTACH, 0, 0, 0 );
}
}
}
上面还是可以通过```fishhook dlsym```来破解,
4. syscall
4.1 syscall直接调用系统函数
//第一个参数是函数的编号,函数编号可以在#import <sys/syscall.h>里找到
//第二个开始是函数的参数,有几个就写几个
syscall(SYS_ptrace, PT_DENY_ATTACH, 0, 0);
4.2 通过内联汇编调用函数
volatile是防止代码被编译器优化掉,先调用syscall,间接调用ptrace
asm volatile(
"mov x0,#26\n"
"mov x1,#31\n"
"mov x2,#0\n"
"mov x3,#0\n"
"mov x16,#0\n"//中断根据x16 里面的值,跳转syscall
"svc #0x80\n"//这条指令就是触发中断(系统级别的跳转!)
);
也可以直接通过软汇编直接调用ptrace
asm volatile(
"mov x0,#31\n"
"mov x1,#0\n"
"mov x2,#0\n"
"mov x3,#0\n"
"mov x16,#26\n"//中断根据x16 里面的值,跳转ptrace
"svc #0x80\n"//这条指令就是触发中断(系统级别的跳转!)
);
}
5. 越狱检测
//越狱检测
char * dlname = getenv("DYLD_INSERT_LIBRARIES");
if (dlname) {
NSLog(@"越狱手机,关闭部分功能");
}else{
NSLog(@"正常手机!");
}
网友评论