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
안녕하세요, 저는 완전한 초보자입니다. 당신의 글을 보고 다시 구현해보려고 했습니다. 그런데 이 바이너리 파일을 컴파일할 때 다음과 같은 오류가 발생했습니다. 어떻게 해결해야 할지 모르겠습니다:
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