防护
当一个进程被调试时,该进程会有一个标志位来标记他正在被标记。sysctl
函数可用于查看当前进程的信息。
-
使用该函数,需要导入系统库
#import <sys/sysctl.h>
。 -
函数介绍
/* 函数的返回值若为0时,证明没有错误,其他数字为错误码。 arg1 传入一个数组,该数组中的第一个元素指定本请求定向到内核的哪个子系统。第二个及其后元素依次细化指定该系统的某个部分。 arg2 数组中的元素数目 arg3 一个结构体,指向一个供内核存放该值的缓冲区,存放进程查询结果 arg4 缓冲区的大小 arg5/arg6 为了设置某个新值,arg5参数指向一个大小为arg6参数值的缓冲区。如果不准备指定一个新值,那么arg5应为一个空指针,arg6因为0. */ int sysctl(int *, u_int, void *, size_t *, void *, size_t); 复制代码
-
函数使用
int name[4];//存放字节码,查询信息 name[0] = CTL_KERN;//内核查看 name[1] = KERN_PROC;//进程查看 name[2] = KERN_PROC_PID//进程ID name[3] = getpid();//获取pid,据说这个可以直接传0??? struct kinfo_proc info;//接受进程查询结果信息的结构体 size_t info_size = sizeof(info);//结构体的大小 int proc_err = sysctrl(name, sizeof(name)/sizeof(*name), info, info_size, 0, 0); assert(proc_err == 0);//出现异常,出发断言 //info.kp_proc.p_flag中存放的是标志位(二进制),在proc.h文件中有p_flag的宏定义,通过&运算可知对应标志位的值是否为0。(若结果值为0则对应标志位为0)。其中P_TRACED为正在跟踪调试过程。 ((info.kp_proc.p_flag & P_TRACED) != 0)的结果即为是否处于调试状态。 复制代码
进攻
可以通过fishhook交换掉系统的sysctl函数,源码很少,网上分析很多。关于fishhook的更多东西,具体请百度,这里直接使用。
代码如下:
//函数指针,保存原方法。
int (*sysctl_p)(int *, u_int, void *, size_t *, void *, size_t);
//用来交换的新方法
int mySysctl(int *name, u_int namelen, void *info, size_t *infosize, void *newinfo, size_t newinfosize){
int err = sysctl_p(name, namelen, info, infosize, newinfo, newinfosize);
//判断是否是查询进程信息
if (namelen == 4
&&name[0] == CTL_KERN
&&name[1] == KERN_PROC
&&name[2] == KERN_PROC_PID
&&info
&&((int)*infosize == sizeof(struct kinfo_proc))) {
//接受结果的结构体
struct kinfo_proc* sys_info = (struct kinfo_proc *)info;
//通过标志位确认是否为调试状态
if ((sys_info->kp_proc.p_flag & P_TRACED) != 0) {
//进行异或运算修改标志位
sys_info->kp_proc.p_flag ^= P_TRACED;
}
}
return err;
}
复制代码
然后在+(void)load;
方法中交换系统的sysctl
方法
rebind_symbols((struct rebinding[1]){{"sysctl",mySysctl,(void *)&sysctl_p}}, 1);
复制代码
防护
对于fishhook交换系统函数的进攻方式,我们可以通过将sysctl函数调用放到动态库中,以保证检测函数可以在进攻注入的代码之前执行。动态库的加载顺序为Build Phases
下的Link Binary With Libaraies
中的排列顺序。