🤛🏽

sysctl

 
ptrace와 비슷한 맥락이다.
다만 이번에는 'P_TRACED 플래그'가 설정됐는지 여부를 확인하는 것.
Is_debugger_present를 알고 있다면 동일하다고 생각하면 된다.
 
  • 정보 : 디버거로 자식으로 생성하건, attach를 하건 P_TRACED 플래그는 Set 된다.
 
아래 코드는 mobile-security-testing-guide의 ios-testing-guide에서 발췌함.
#include <assert.h> #include <stdbool.h> #include <sys/types.h> #include <unistd.h> #include <sys/sysctl.h> 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 ); }
배열 설정하고 sysctl를 호출하면 결과가 info에 담긴다.
결과로 얻은 info에 P_TRACED 플래그가 있는지 없는지 확인함으로써 디버깅 여부를 판단한다.
 
위 소스는 딱 봐도 우회하기 좋게 생겼다.
또한 sysctl을 호출한다는 사실로 앱이 디버깅 감지를 수행한다는 판단의 지표가 될 수도 있다.
 

[우회]

검증 함수 패치 혹은 후킹은 여태 하던대로 하면 된다.
디버거 상에서 실시간으로 우회할 때 편하게 접근하는 방법만 다룬다.
 
(gdb) break sysctl if $r1==4 && *(int *)$r0==1 && *(int *)($r0+4)==14 && *(int *)($r0+8)==1
특정 인자를 가진 sysctl에서 중단점을 설정하는 명령어이다.
ios에는 $r0~$r7가 인자 전달로 쓰이며 $r0부터 순서대로 들어간다고 보면 된다.
 
*(int *)$r0==1 (첫번째인자[0] == 1 : CTL_KERN)*(int *)($r0+4)==14 (첫번째인자[1] == 14 : KERN_PROC)*(int *)($r0+8)==1 (첫번째인자[2] == 1 : KERN_PROC_PID)$r1==4 (두번째인자 == 4)
두번째 인자는 sizeof(mib)/sizeof(*mib)으로 배열의 총 크기를 한 요소의 크기로 나누는 것이므로 배열의 개수다.
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
이렇게 총 4개를 넘기므로, 두번째 인자에 배열의 크기인 4가 담겼는지를 확인하는 것.
 
(gdb) set $pinfo=$r2
우회하려면 결과로 받아온 플래그에서 P_TRACED 플래그를 제거해야하므로 세번째 인자인 kinfo_proc 구조체를 담아둔다.
 
(gdb) finish (gdb) break *$pc if $pinfo!=-1
finish명령으로 sysctl 호출 끝까지 실행시킨 후, 받아온 info에 제대로 정보가 담겼다면 bp를 건다.
정보를 담아온 다음이므로 잡아서 값을 바꾸면 됨.
 
(gdb) set $pflag = (*(int *)($pinfo+16)) (gdb) set *(int *)($pinfo+16) = $pflag & ~0x800
플래그 부분을 가져와서 0x800(P_TRACED) 플래그만 제거해서 다시 설정한다.
 
(gdb) print (*(int *)($pinfo+16) & 0x800) (gdb) continue
print 출력 결과 0으로 나온다면 제대로 P_TRACED 플래그를 제거한 것.
끝.

[자동 우회 설정]

매번 bp 걸릴 때마다 저 짓을 하기는 번거로우니, commands 명령어로 bp걸렸을 때 수행될 명령어를 등록하자.
 
commands 1 silent set $pinfo=$r2 continue end
commands 2 silent set $pflag = (*(int *)($pinfo+16)) set *(int *)($pinfo+16) = $pflag & ~0x800 set $pinfo=-1 continue end