iOS逆向安防从入门到秃头--基础防护 & 破解

1,067 阅读3分钟

小谷秃头合集

  • 今天小谷分享几种 基础的 防护手段。— 当然,也说下如何破解

1. 白名单防护

  • 这个防护目前应该是用的最少的,原因是这个误伤比较大

思前想后,还是先要说下这种防护手段

  • 原理: 白名单防护手段的原理主要是防止动态库的插入

也就是把所有可能用到的动态库收集起来。如果检测到多余的动态库,就可以做相应的防护技巧

  • 第一步: 获取执行所需要的所有库
//获取image 个数
int count = _dyld_image_count();
//把自己的macho镜像去掉
for (int i = 1; i < count; i++) {
    //打印所有image name,用,隔开
    printf("%s,",_dyld_get_image_name(i));
}
  • 第二步: 把输出的logcopy

1.png

如果以后真的用到了,绝对不能写在客户端。(其实就算写在服务器请求误伤还是很大!)

  • 第三步: 判断应用加载进入的时候,是否有不包含的库(这里如果存在,就视为注入的库)
    int count = _dyld_image_count();
    //把自己的macho镜像去掉
    for (int i = 1; i < count; i++) {
//        printf("%s,",_dyld_get_image_name(i));
        if (!strstr(libStr, _dyld_get_image_name(i))) {
            //如果不包含,视为注入库!
            NSLog(@"注入库。采取防护措施~!");
        }
    }
  • 总结下
      1. 白名单防护误伤太大
      1. 现在基本没有啥应用会这么做。维护成本太高
      1. 如果系统升级,或者每个机型的系统库不一样。就要一直改变兼容
      1. 就算写成服务器存储。也可能不是第一瞬间判断的

不推荐这种防护!,但是通过白名单防护,可能会有很多延伸的思想。兄弟们可以考虑下~

2. Ptrace

很早之前支付宝就是用的这个~

2.1. Ptrace防护

  • 我们逆向APP的时候,有时候不仅仅是静态分析,还可能会动态调试

  • 我们的动态调试,无论是终端调试,还是Xcode附加。都是通过debugserver监测进程做到的!

  • Ptrace就是通过限制进程阻止附加调试

  • 先来个Ptrace的例子

//点击事件来一波
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"ptrace 防调试!");
    ptrace(PT_DENY_ATTACH, 0, 0, 0);//这个就是Ptrace防护调试
    NSLog(@"难道没有防护住?");
}
  • 我们点击一下

2.png

发现之后就不走了!

Ptrace特点:调试闪退,但是点击APP正常运行

2.2. Ptrace破解

防护办法就有破解办法

  • 如果你确定是Ptrace防护了。那么我们可以hook这个符号试试~
//定义函数指针!
int  (*ptrace_p)(int _request, pid_t _pid, caddr_t _addr, int _data);

+(void)load
{
    //交换
    struct rebinding ptraceBd;
    ptraceBd.name ="ptrace";
    ptraceBd.replacement = my_ptrace;
    ptraceBd.replaced = (void *)&ptrace_p;
    
    struct rebinding bds[] = {ptraceBd};
    rebind_symbols(bds, 1);
}
//自定义
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 0;
}

利用fishhook符号重绑定就搞定了~

3. sysctl

3.1. sysctl防护

这个目前还是挺厉害的~ 抖音就是用的这个,不过他除了这个还做了别的防护(改天我玩一下,然后写一篇博客,😆)

  • 写一下他的防护代码
#import <sys/sysctl.h>
bool isDebugServer(){
    //控制码
    int name[4];//放字节码-查询信息
    name[0] = CTL_KERN;//内核查看
    name[1] = KERN_PROC;//查询进程
    name[2] = KERN_PROC_PID; //通过进程id查进程
    name[3] = getpid();//拿到自己进程的id
    //查询结果
    struct kinfo_proc info;//进程查询信息结果
    size_t info_size = sizeof(info);//结构体大小
    int error = sysctl(name, sizeof(name)/sizeof(*name), &info, &info_size, 0, 0);
    assert(error == 0);//0就是没有错误,设置个断言
    //结果解析 p_flag的第12位为1就是有调试
    //p_flag 与 P_TRACED =0 就是有调试
    return ((info.kp_proc.p_flag & P_TRACED) !=0);
}
- (void)viewDidLoad {
    [super viewDidLoad];
    if (isDebugServer()) {
        NSLog(@"在debugserver调试状态!");
        //自行处理
    }else{
        NSLog(@"在正常运行状态!");
    }
}
  • 当我Xcode 运行

3.png

  • 当我断开xcode,自己运行时

4.png

说明检测到了

3.2. sysctl破解

也是用的fishhook符号重绑定

  • 这里我就写重绑定之后的实现
//自定义函数
int mySysctl(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);
        //拿出info做判断
        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 0;
    }
    return sysctl_p(name, namelen, info, infosize, newinfo, newinfosize);
}

4. 总结

  • 白名单防护是一种思维手段,这种防护误伤很大,慎用!!

  • Ptracesysctl现在用的还挺多的,你要想办法做到神不知鬼不觉~

  • 大家可以用到工程试上一下~ 😆。