CInt let ptrace = unsafeBitCast(ptracePtr, to: PtraceType.self) // PT_DENY_ATTACH == 31 let ptraceRet = ptrace(31, 0, 0, 0) if ptraceRet != 0 { print("Error occured when calling ptrace(). Denying debugger may not be reliable") } }"> CInt let ptrace = unsafeBitCast(ptracePtr, to: PtraceType.self) // PT_DENY_ATTACH == 31 let ptraceRet = ptrace(31, 0, 0, 0) if ptraceRet != 0 { print("Error occured when calling ptrace(). Denying debugger may not be reliable") } }"> CInt let ptrace = unsafeBitCast(ptracePtr, to: PtraceType.self) // PT_DENY_ATTACH == 31 let ptraceRet = ptrace(31, 0, 0, 0) if ptraceRet != 0 { print("Error occured when calling ptrace(). Denying debugger may not be reliable") } }">

<aside> ✨

在代码中嵌入检测逻辑,防止应用被调试器附加或遭受注入攻击。

一旦检测到调试行为,可以触发相应的保护措施,如停止运行或清除敏感数据。

</aside>

static func denyDebugger() {
    // bind ptrace()
    let pointerToPtrace = UnsafeMutableRawPointer(bitPattern: -2)
    let ptracePtr = dlsym(pointerToPtrace, "ptrace")
    typealias PtraceType = @convention(c) (CInt, pid_t, CInt, CInt) -> CInt
    let ptrace = unsafeBitCast(ptracePtr, to: PtraceType.self)
    
    // PT_DENY_ATTACH == 31
    let ptraceRet = ptrace(31, 0, 0, 0)
    
    if ptraceRet != 0 {
      print("Error occured when calling ptrace(). Denying debugger may not be reliable")
    }
}

/**
    ptrace
    ptrace + svc
    ptrace + xor
 */
- (void)ori_ptrace {
    ptrace(PT_DENY_ATTACH, 0, 0, 0);
}

- (void)svc_ptrace {
#ifdef __arm64__
    __asm__("mov X0, #31\\n"
            "mov X1, #0\\n"
            "mov X2, #0\\n"
            "mov X3, #0\\n"
            "mov w16, #26\\n"
            "svc #0x80");
#endif
}

- (void)xor_ptrace {
    
    //A 异或 B 等到 C,C 再异或 A 得到 B,隐藏 ptrace
    unsigned char str[] = {
        ('q' ^ 'p'),
        ('q' ^ 't'),
        ('q' ^ 'r'),
        ('q' ^ 'a'),
        ('q' ^ 'c'),
        ('q' ^ 'e'),
        ('q' ^ '\\0')
    };
    
    unsigned char *p = str;
    while (((*p) ^= 'q') != '\\0')
        p++;
    
    int (*ptrace_ptr)(int _request, pid_t _pid, caddr_t _addr, int _data);
    void *handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);   // 获得句柄
    ptrace_ptr = dlsym(handle, (const char *)str);      // 动态查找 ptrace 符号
    ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
    dlclose(handle);
}

/**
    syscall
    syscall + ptrace + svc
    syscall + ptrace + xor
 */
- (void)ori_syscall {
    syscall(SYS_ptrace, PT_DENY_ATTACH, 0, 0, 0);
}

- (void)svc_syscall {
#ifdef __arm64__
    __asm__("mov X0, #26\\n"
            "mov X1, #31\\n"
            "mov X2, #0\\n"
            "mov X3, #0\\n"
            "mov X4, #0\\n"
            "mov w16, #0\\n"
            "svc #0x80");
#endif
}

- (void)xor_syscall {
    
    //A 异或 B 等到 C,C 再异或 A 得到 B,隐藏 syscall
    unsigned char str[] = {
        ('q' ^ 's'),
        ('q' ^ 'y'),
        ('q' ^ 's'),
        ('q' ^ 'c'),
        ('q' ^ 'a'),
        ('q' ^ 'l'),
        ('q' ^ 'l'),
        ('q' ^ '\\0')
    };
    
    unsigned char *p = str;
    while (((*p) ^= 'q') != '\\0')
        p++;
    
    int (*syscall_ptr)(int number, ...);
    void *handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);   // 获得句柄
    syscall_ptr = dlsym(handle, (const char *)str);     // 动态查找 syscall 符号
    syscall_ptr(SYS_ptrace, PT_DENY_ATTACH, 0, 0, 0);
    dlclose(handle);
}

/**
    sysctl
    sysctl  + svc
    sysctl  + xor
 */
- (int)ori_sysctl {
    
    // 需要检测进程信息的字段数组
    int name[4];                // 里面存放字节码,查询信息
    name[0] = CTL_KERN;         // 内核查询
    name[1] = KERN_PROC;        // 进程查询
    name[2] = KERN_PROC_PID;    // 传递的参数是进程的ID(PID) 同:$ ps -A
    name[3] = getpid();         // 获取 pid,据说这个可以直接传 0

    struct kinfo_proc info;             // 接受进程查询结果信息的结构体
    size_t info_size = sizeof(info);    // 结构体的大小
    info.kp_proc.p_flag = 0;

    /* 查询成功返回 0 */
    sysctl(name, sizeof(name) / sizeof(*name), &info, &info_size, NULL, 0);
    
    /*
        查询结果看info.kp_proc.p_flag 的第12位,如果为1,表示调试附加状态。
        info.kp_proc.p_flag & P_TRACED 即可获取第12位
    */
    return ((info.kp_proc.p_flag & P_TRACED) != 0);
}

- (int)svc_sysctl {
    
    // 需要检测进程信息的字段数组
    int name[4];                // 里面存放字节码,查询信息
    name[0] = CTL_KERN;         // 内核查询
    name[1] = KERN_PROC;        // 进程查询
    name[2] = KERN_PROC_PID;    // 传递的参数是进程的ID(PID) 同:$ ps -A
    name[3] = getpid();         // 获取 pid,据说这个可以直接传 0
    
    struct kinfo_proc proc;
    size_t proc_size = sizeof(proc);
    memset(&proc, 0, proc_size);
    
#ifdef __arm64__
    __asm__(
        "mov x0, %[name_ptr]\\n"
        "mov x1, #4\\n"
        "mov x2, %[proc_ptr]\\n"
        "mov x3, %[size_ptr]\\n"
        "mov x4, #0x0\\n"
        "mov x5, #0x0\\n"
        "mov w16, #202\\n"
        "svc #0x80\\n"
        
        :
        
        :[name_ptr]"r"(&name), [proc_ptr]"r"(&proc), [size_ptr]"r"(&proc_size)
        
    );
#endif
    
    /*
        查询结果看info.kp_proc.p_flag 的第12位,如果为1,表示调试附加状态。
        info.kp_proc.p_flag & P_TRACED 即可获取第12位
    */
    return ((proc.kp_proc.p_flag & P_TRACED) != 0);
}

- (int)xor_sysctl {
    
    int name[4];                    // 里面存放字节码,查询信息
    name[0] = CTL_KERN;             // 内核查询
    name[1] = KERN_PROC;            // 进程查询
    name[2] = KERN_PROC_PID;        // 传递的参数是进程的ID(PID) 同:$ ps -A
    name[3] = getpid();             // 获取当前进程ID
    
    struct kinfo_proc info;
    size_t info_size = sizeof(info);
    info.kp_proc.p_flag = 0;
    
    //A 异或 B 等到 C,C 再异或 A 得到 B,隐藏 sysctl
    unsigned char str[] = {
        ('q' ^ 's'),
        ('q' ^ 'y'),
        ('q' ^ 's'),
        ('q' ^ 'c'),
        ('q' ^ 't'),
        ('q' ^ 'l'),
        ('q' ^ '\\0')
    };
    
    unsigned char *p = str;
    while (((*p) ^= 'q') != '\\0')
        p++;
    
    int (*sysctl_ptr)(int *, u_int, void *, size_t *, void *, size_t);
    void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);       // 获得句柄
    sysctl_ptr = dlsym(handle, (const char *)str);          // 动态查找 sysctl 符号
    sysctl_ptr(name, 4, &info, &info_size, 0, 0);
    dlclose(handle);
    
    /*
        查询结果看info.kp_proc.p_flag 的第12位,如果为1,表示调试附加状态。
        info.kp_proc.p_flag & P_TRACED 即可获取第12位
    */
    return ((info.kp_proc.p_flag & P_TRACED) != 0);
}