iOS 逆向 Ptrace 反调试

2019-12-12  本文已影响0人  Ray0218

attribute((always_inline)) (强内联)

的意思是强制内联,所有加了attribute((always_inline)) 的函数再被调用时不会被编译成函数调用而是直接扩展到调用函数体内,比如

__attribute__((always_inline)) void a(){
    print(" function a"); 
}

void b()
{
  a();
}

编译以后就是:

 void b()
{
    print(" function a"); 
}

反调试检测


#import <dlfcn.h>
#import <sys/types.h>

void disable_gdb() {
    // 方式一   系统函数并没有暴露出此方法所以不能直接通过此方式调用
    // ptrace(PT_DENY_ATTACH, 0, 0, 0);
    
    //    方式二 通过dlopen,dlsym调用
    void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
    ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
    ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
    dlclose(handle);
}
#import <sys/syscall.h>
void AntiDebug_005() { 
syscall(SYS_ptrace, PT_DENY_ATTACH, 0, 0, 0); 
}

其实这种方法等同于直接使用 ptrace, 此时系统调用号是 SYS_ptrace


static __attribute__((always_inline)) void AntiDebug_003() {
#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
}

其实这种方法等同于使用 syscall(SYS_ptrace, PT_DENY_ATTACH, 0, 0, 0), 这里需要注意, 此时的系统调用号是 0, 也就是 SYS_syscall

static __attribute__((always_inline)) void AntiDebug_004() {
#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

}

使用 sysctl 检测

sysctl命令被用于在内核运行时动态地修改内核的运行参数
函数原型
int sysctl (int *name, int nlen, void *oldval, size_t *oldlenp, void *newval, size_t newlen);

Name /* 整形数组,每个数组元素代表系统参数存取路径上的一个文件或目录名,例如/proc/sys/kernel用CTL_KERN表示*/

oldval /* 当读取系统参数时,用于存取系统参数值,也就是/proc/sys/下的某个文件内容*/

Newval /* 当写系统参数时,记录所要写入的新值*/

用于反调试原理:
当一个进程被调试的时候,该进程会有一个标记来标记自己正在被调试,所以可以通过sysctl去查看当前进程的信息,看有没有这个标记位即可检查当前调试状态。


BOOL isDebugger(){
    //控制码
    int name[4];//里面放字节码.查询信息
    name[0] = CTL_KERN;     //内核查看
    name[1] = KERN_PROC;    //查询进程
    name[2] = KERN_PROC_PID;//传递的参数是进程的ID(PID)   //同:$ ps -A
    name[3] = getpid();     //PID的值告诉(进程id)
    
    struct kinfo_proc info; //接受进程查询结果信息的结构体
    size_t info_size = sizeof(info);//结构体的大小
    //int error = sysctl(name, 4, &info, &info_size, 0, 0);
    int error = sysctl(name, sizeof(name)/sizeof(*name), &info, &info_size, 0, 0);
    assert(error == 0);//0就是没有错误,其他就是错误码
    //1011 1000 1010 1010 1101 0101 1101 0101
    //&
    //0000 0000 0000 1000 0000 0000 0000 0000
    // == 0 ? 没有、有!!
    //    p_flag的第12位位1就是有调试
    //    p_flag 与P_TRACED = 0就是有调试
    return ((info.kp_proc.p_flag & P_TRACED) != 0);  // P_TRACED: 跟踪调试过程
}

汇编方式写法

__attribute__((always_inline)) bool checkTracing() {
    size_t size = sizeof(struct kinfo_proc);
    struct kinfo_proc proc;
    memset(&proc, 0, size);
    
    //控制码
    int name[4];//里面放字节码.查询信息
    name[0] = CTL_KERN;     //内核查看
    name[1] = KERN_PROC;    //查询进程
    name[2] = KERN_PROC_PID;//传递的参数是进程的ID(PID)   //同:$ ps -A
    name[3] = getpid();     //PID的值告诉(进程id)
    
    __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"(&size)
            
            );
    
    return (proc.kp_proc.p_flag & P_TRACED);
    
}

检测到调试器就退出,或者制造崩溃,或者隐藏工程啥的,当然也可以定时去查看有没有这个标记,

利用内联汇编实现退出

很多检测到调试器后使用 exit(-1) 退出程序. 这里很容易让 cracker 断点到 exit 函数上,通过汇编清除堆栈

static __attribute__((always_inline)) void asm_exit() {
#ifdef __arm64__
    __asm__("mov X0, #0\n"
            "mov w16, #1\n"
            "svc #0x80\n"
            
            "mov x1, #0\n"
            "mov sp, x1\n"
            "mov x29, x1\n"
            "mov x30, x1\n"
            "ret");
#endif
}

链接:https://www.jianshu.com/p/c5b9cad9b873
https://www.jianshu.com/p/a3fc10c70a29

上一篇 下一篇

猜你喜欢

热点阅读