1. ptrace

    코드

    void svc80_anti_debug(void) {
    #if defined __arm64__
        __asm __volatile("mov x0, #26");
        __asm __volatile("mov x1, #31");
        __asm __volatile("mov x2, #0");
        __asm __volatile("mov x3, #0");
        __asm __volatile("mov x16, #0");
        __asm __volatile("svc #0x80");
    #endif
    }

    작동 원리

    int ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval)
    {
        ...
        if (uap->req == PT_DENY_ATTACH) {
    		if (ISSET(p->p_lflag, P_LTRACED)) {
    			proc_unlock(p);
    			KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_FRCEXIT) | DBG_FUNC_NONE,
    			    p->p_pid, W_EXITCODE(ENOTSUP, 0), 4, 0, 0);
    			exit1(p, W_EXITCODE(ENOTSUP, 0), retval);
    
    			thread_exception_return();
    			/* NOTREACHED */
    		}
    		SET(p->p_lflag, P_LNOATTACH);
    		proc_unlock(p);
    
    		return 0;
    	}
        ...
        if (uap->req == PT_ATTACH) {
            int             err;
            ...
            /* not allowed to attach, proper error code returned by kauth_authorize_process */
    		if (ISSET(t->p_lflag, P_LNOATTACH)) {
    			psignal(p, SIGSEGV);
    		}
            ...
        }
        ...
    }

    proc 구조체의 p_lflag에 P_LTRACED가 세트되어있으면, ENOTSUP(-45)와 함께 디버그할려는 프로세스를 종료시켜버린다. (디버깅 중에 ptrace 함수가 수행된 경우)

    P_LNOATTACH가 세트되어 있을 경우에는, 디버그할려는 프로세스를 종료시키지는 않지만
    SIGSEGV 시그널과 함께 디버거 프로세스를 종료시켜버린다. (ptrace 함수가 수행된 후에 디버그를 시도할 경우)

    Reference

    https://github.com/apple/darwin-xnu/blob/main/bsd/kern/mach_process.c#L133

    https://github.com/apple/darwin-xnu/blob/main/bsd/sys/errno.h#L146

    https://github.com/apple/darwin-xnu/blob/main/bsd/kern/mach_process.c#L308


    2. sysctl

    코드

    static bool AmIBeingDebugged(void)
        // Returns true if the current process is being debugged (either
        // running under the debugger or has a debugger attached post facto).
    {
        int                 junk;
        int                 mib[4];
        struct kinfo_proc   info;
        size_t              size;
    
        // Initialize the flags so that, if sysctl fails for some bizarre
        // reason, we get a predictable result.
    
        info.kp_proc.p_flag = 0;
    
        // Initialize mib, which tells sysctl the info we want, in this case
        // we're looking for information about a specific process ID.
    
        mib[0] = CTL_KERN;
        mib[1] = KERN_PROC;
        mib[2] = KERN_PROC_PID;
        mib[3] = getpid();
    
        // Call sysctl.
    
        size = sizeof(info);
        junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
        assert(junk == 0);
    
        // We're being debugged if the P_TRACED flag is set.
    
        return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
    }

    작동 원리

    STATIC void
    fill_user64_externproc(proc_t p, struct user64_extern_proc *__restrict exp)
    {
        ...
        if (p->p_lflag & P_LTRACED) {
    		exp->p_flag |= P_TRACED;
    	}
        ...
    }

    proc 구조체의 p_lflag에 P_LTRACED가 세트되어있으면,
    마찬가지로 extern_proc 구조체의 p_lflag에 P_TRACED가 세트된다.

    Reference

    https://github.com/apple/darwin-xnu/blob/main/bsd/kern/kern_sysctl.c#L1079


    3. getppid

    코드

    if(getppid() != 1) {
        return YES;    //detected debugger;
    }

    부모 pid가 launchd 프로세스인지 확인한다.

    launchd 프로세스의 pid는 항상 1이므로,
    1이 아닌 경우 디버거가 탐지된걸로 판단한다.

    작동 원리

    int getppid(proc_t p, __unused struct getppid_args *uap, int32_t *retval)
    {
    	*retval = p->p_ppid;
    	return 0;
    }

    proc 구조체의 p_ppid 값을 반환시킨다.

    Reference

    https://github.com/apple/darwin-xnu/blob/main/bsd/sys/proc_internal.h#L463

    https://github.com/apple/darwin-xnu/blob/main/bsd/kern/kern_prot.c#L189


    Bypass!

    uint64_t getProc(pid_t pid) {
        //  https://github.com/apple/darwin-xnu/blob/main/bsd/sys/proc_internal.h#L193
        //  https://github.com/apple/darwin-xnu/blob/main/bsd/sys/queue.h#L470
        
        uint64_t proc = kread64(kernproc);
        
        while (true) {
            if(kread32(proc + 0x68/*PROC_P_PID_OFF*/) == pid) {
                return proc;
            }
            proc = kread64(proc + 0x8/*PROC_P_LIST_LE_PREV_OFF*/);
        }
        
        return 0;
    }
    
    //https://stackoverflow.com/questions/49506579/how-to-find-the-pid-of-any-process-in-mac-osx-c
    int find_pids(const char *name)
    {
    	int ret = -1;
        pid_t pids[2048];
        int bytes = proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(pids));
        int n_proc = bytes / sizeof(pids[0]);
        for (int i = 0; i < n_proc; i++) {
            struct proc_bsdinfo proc;
            int st = proc_pidinfo(pids[i], PROC_PIDTBSDINFO, 0,
                                 &proc, PROC_PIDTBSDINFO_SIZE);
            if (st == PROC_PIDTBSDINFO_SIZE) {
                if (strcmp(name, proc.pbi_name) == 0) {
                    /* Process PID */
                    // printf("%d [%s] [%s]\n", pids[i], proc.pbi_comm, proc.pbi_name);     
    				return pids[i];           
                }
            }       
        }
    	return ret;
    }
    
    #define P_LNOATTACH     0x00001000 
    #define P_LTRACED       0x00000400
    
    #define ISSET(t, f)     ((t) & (f))
    #define CLR(t, f)       (t) &= ~(f)
    #define SET(t, f)       (t) |= (f)
    
    int main(int argc, char *argv[], char *envp[]) {
    	@autoreleasepool {
    		libjb = dlopen("/var/jb/basebin/libjailbreak.dylib", RTLD_NOW);
    
    		if(dimentio_init(0, NULL, NULL) != KERN_SUCCESS) {
        		printf("failed dimentio_init!\n");
    			return 1;
      		}
    
    		if(kbase == 0) {
    			printf("failed get_kbase\n");
    			return 1;
    		}
    
    		uint64_t kslide = kbase - 0xFFFFFFF007004000;
    		printf("[i] kbase: 0x%llx, kslide: 0x%llx\n", kbase, kslide);
    		printf("[i] kread64 from base: 0x%llx\n", kread64(kbase));
    
    		int ptracetest_pid = find_pids("ptracetest");
    		printf("[i] ptracetest pid: %d\n", ptracetest_pid);
    		if(ptracetest_pid == -1) {
    			printf("Not running ptracetest.\n");
    			return 1;
    		}
    
    		uint64_t ptracetest_proc = getProc(ptracetest_pid);
    		printf("[i] ptracetest proc: 0x%llx\n", ptracetest_proc);
    		
    		// https://github.com/apple/darwin-xnu/blob/main/bsd/kern/mach_process.c#L133
    		uint64_t ptracetest_lflag = ptracetest_proc + 0x1c0/*lflagoffset*/;
    		unsigned int lflagvalue = kread32(ptracetest_lflag);
    		printf("[i] ptracetest proc->p_lflag: 0x%x\n", lflagvalue);
    
    		if(ISSET(lflagvalue, P_LNOATTACH))
            {
                printf("[+] P_LNOATTACH has been set, clearing...\n");
                CLR(lflagvalue, P_LNOATTACH);
            	kwrite32(ptracetest_lflag, lflagvalue);
    			printf("[+] P_LNOATTACH now unset.\n");
    
    			lflagvalue = kread32(ptracetest_lflag);
    			printf("[+] ptracetest proc->p_lflag: 0x%x\n", lflagvalue);
            }
    
    		// https://github.com/apple/darwin-xnu/blob/main/bsd/kern/kern_sysctl.c#L1079
    		if(argc == 2 && strcmp(argv[1], "notrace") == 0){
    			if(ISSET(lflagvalue, P_LTRACED))
            	{
                	printf("[+] P_LTRACED has been set, clearing...\n");
                	CLR(lflagvalue, P_LTRACED);
            		kwrite32(ptracetest_lflag, lflagvalue);
    				printf("[+] P_LTRACED now unset.\n");
    
    				lflagvalue = kread32(ptracetest_lflag);
    				printf("[+] ptracetest proc->p_lflag: 0x%x\n", lflagvalue);
            	}
    		}
    		
    		if(argc == 2 && strcmp(argv[1], "trace") == 0) {
    			if(!ISSET(lflagvalue, P_LTRACED))
            	{
                	printf("[+] P_LTRACED has NOT been set, setting...\n");
                	SET(lflagvalue, P_LTRACED);
            		kwrite32(ptracetest_lflag, lflagvalue);
    				printf("[+] P_LTRACED now set.\n");
    
    				lflagvalue = kread32(ptracetest_lflag);
    				printf("[+] ptracetest proc->p_lflag: 0x%x\n", lflagvalue);
            	}
    		}
    
    		uint64_t ptracetest_ppid = ptracetest_proc + 0x20;
    		unsigned int ppidvalue = kread32(ptracetest_ppid);
    		printf("[i] ptracetest proc->p_ppid: %d\n", ppidvalue);
    		if(ppidvalue != 1) {
    			printf("[+] Patching proc->p_ppid to 1...\n");
    			kwrite32(ptracetest_ppid, 1);
    
    			ppidvalue = kread32(ptracetest_ppid);
    			printf("[+] ptracetest proc->p_ppid: %d\n", ppidvalue);
    		}
    
    		dlclose(libjb);
    
    		return 0;
    	}
    }

    간단하게 그냥 proc 구조체의 p_lflag에 세트된 P_LNOATTACH, P_LTRACED를 지우고
    p_ppid 값을 1로 덮어씌우면 된다.


    Demo

    테스트 환경

    iPhone SE (2nd generation) / iOS 15.0.2 / Dopamine

    One Comment

    1. 안녕하세요, 저는 완전한 초보자입니다. 당신의 글을 보고 다시 구현해보려고 했습니다. 그런데 이 바이너리 파일을 컴파일할 때 다음과 같은 오류가 발생했습니다. 어떻게 해결해야 할지 모르겠습니다:
      dimentio.c:7:21: error: call to undeclared function ‘kread64’; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
      uint64_t proc = kread64(kernproc);
      ^
      dimentio.c:7:29: error: use of undeclared identifier ‘kernproc’
      uint64_t proc = kread64(kernproc);
      ^
      dimentio.c:10:12: error: call to undeclared function ‘kread32’; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
      if(kread32(proc + 0x68/*PROC_P_PID_OFF*/) == pid) {
      ^
      dimentio.c:13:16: warning: implicit conversion changes signedness: ‘int’ to ‘uint64_t’ (aka ‘unsigned long long’) [-Wsign-conversion]
      proc = kread64(proc + 0x8/*PROC_P_LIST_LE_PREV_OFF*/);
      ~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      dimentio.c:3:10: warning: no previous prototype for function ‘getProc’ [-Wmissing-prototypes]
      uint64_t getProc(pid_t pid) {
      ^
      dimentio.c:3:1: note: declare ‘static’ if the function is not intended to be used outside of this translation unit
      uint64_t getProc(pid_t pid) {
      ^
      static
      dimentio.c:24:17: error: call to undeclared function ‘proc_listpids’; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
      int bytes = proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(pids));
      ^
      dimentio.c:24:31: error: use of undeclared identifier ‘PROC_ALL_PIDS’
      int bytes = proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(pids));
      ^
      dimentio.c:25:18: warning: implicit conversion changes signedness: ‘int’ to ‘unsigned long’ [-Wsign-conversion]
      int n_proc = bytes / sizeof(pids[0]);
      ^~~~~ ~
      dimentio.c:27:29: error: variable has incomplete type ‘struct proc_bsdinfo’
      struct proc_bsdinfo proc;
      ^
      dimentio.c:27:16: note: forward declaration of ‘struct proc_bsdinfo’
      struct proc_bsdinfo proc;
      ^
      dimentio.c:28:18: error: call to undeclared function ‘proc_pidinfo’; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
      int st = proc_pidinfo(pids[i], PROC_PIDTBSDINFO, 0,
      ^
      dimentio.c:28:40: error: use of undeclared identifier ‘PROC_PIDTBSDINFO’
      int st = proc_pidinfo(pids[i], PROC_PIDTBSDINFO, 0,
      ^
      dimentio.c:29:37: error: use of undeclared identifier ‘PROC_PIDTBSDINFO_SIZE’
      &proc, PROC_PIDTBSDINFO_SIZE);
      ^
      dimentio.c:30:19: error: use of undeclared identifier ‘PROC_PIDTBSDINFO_SIZE’
      if (st == PROC_PIDTBSDINFO_SIZE) {
      ^
      dimentio.c:20:5: warning: no previous prototype for function ‘find_pids’ [-Wmissing-prototypes]
      int find_pids(const char *name)
      ^
      dimentio.c:20:1: note: declare ‘static’ if the function is not intended to be used outside of this translation unit
      int find_pids(const char *name)
      ^
      static
      dimentio.c:49:5: error: expected expression
      @autoreleasepool {
      ^
      dimentio.c:48:14: warning: unused parameter ‘argc’ [-Wunused-parameter]
      int main(int argc, char *argv[], char *envp[]) {
      ^
      dimentio.c:48:26: warning: unused parameter ‘argv’ [-Wunused-parameter]
      int main(int argc, char *argv[], char *envp[]) {
      ^
      dimentio.c:48:40: warning: unused parameter ‘envp’ [-Wunused-parameter]
      int main(int argc, char *argv[], char *envp[]) {
      ^
      7 warnings and 11 errors generated.
      make: *** [all] Error 1
      lzy@lzydeMacBook-Pro dimentio-main % make
      xcrun -sdk iphoneos clang -arch arm64 -mios-version-min=10.0 -Weverything libdimentio.c dimentio.c -o dimentio -framework IOKit -framework CoreFoundation -lcompression -Os

    답글 남기기

    이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다