ios) ptrace관련 안티디버깅 정리
https://alexomara.com/blog/defeating-anti-debug-techniques-macos-ptrace-variants/
[ios 관련 팁]
ptrace로 PT_DENY_ATTACH로 디버깅을 못하게하는 건 가장 기본적인 안티디버깅 방법.
근데 ptrace는 public iOS API가 아니므로 직접적으로 쓰면 앱스토어 검수에서 불통.
따라서 아래 코드처럼 dlsym으로 ptrace 함수의 주소를 알아와서 사용하는 방식으로 사용함.
#import <dlfcn.h>
#import <sys/types.h>
#import <stdio.h>
typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);
void anti_debug() {
ptrace_ptr_t ptrace_ptr = (ptrace_ptr_t)dlsym(RTLD_SELF, "ptrace");
ptrace_ptr(31, 0, 0, 0); // PTRACE_DENY_ATTACH = 31
}
추가 팁 : 종료된 메세지가 exited with status 45(0x2d)라면 ptrace로 인해서 종료됐다는 뜻.
사례1
가장 기본적인 사례.
// clang -o main main.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
ptrace(PT_DENY_ATTACH, 0, 0, 0);
printf("SUCCESS\n");
return 0;
}
1. bp ptrace로 ptrace부분에 중단점 설정하고.
2. ptrace 함수 호출 시의 인자인 PT_DENY_ATTACH는 31(0x1F)인데, 이 값을 0으로 바꿔서 호출시키면 우회 끝.
사례2
exited with status 45인데도 불구하고 bp ptrace가 안먹히는 경우.
이때는 시스템 함수를 직접 호출한 경우임.
// clang -o main main.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
asm(
"pushq %rax\n"
"pushq %rdi\n"
"movq $0x1f, %rdi\n"
"movq $0x200001A, %rax\n"
"syscall\n"
"popq %rdi\n"
"popq %rax\n"
);
printf("SUCCESS\n");
return 0;
}
syscall(0x200001A)
따라서 syscall 호출 순간을 잡아서 0x200001A인 인자를 0으로 바꾸고 syscall 호출하도록 변경하면 우회 끝.
사례3
잘 알려지지 않은 사례
1. ptrace(PT_DENY_ATTACH, 0, 0, 0)이 잘 동작하는 지
2. 이와 관련된 코드가 변경됐는지를 검사
ptrace(PT_DENY_ATTACH, 0, 0, 0 )가 적용된 프로세스에 attach하면 segmentation fault가 발생한다는 점을 이용한 방법.
// clang -o main main.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <unistd.h>
int deny_attach_successful = 0;
void sigsegv_handler(int sig) {
printf("sigsegv_handler: %i\n", sig);
deny_attach_successful = 1;
}
int main(int argc, char *argv[]) {
pid_t pid = getpid();
ptrace(PT_DENY_ATTACH, 0, 0, 0);
signal(SIGSEGV, sigsegv_handler);
ptrace(PT_ATTACH, pid, 0, 0);
if (!deny_attach_successful) {
printf("FAILURE\n");
return 1;
}
printf("SUCCESS\n");
return 0;
}
1. 전역변수는 default 값으로 0(false)으로 설정한 상태.
2. 첫번째 ptrace 호출에서 PT_DENY_ATTACH를 등록하였다.
그러면 이후의 attach 시도 시 segmentation fault가 뜨는 게 정상이다.
3. 따라서 seg fault가 뜨면 전역변수를 1(true)로 설정하는 커스텀 시그널 핸들러를 별도로 등록하고,
4. 자기 자신에게 ptrace attach를 시도한다.
==> 스스로에게 attach 시도한 것이 첫번째 ptrace에 의해 정상적으로 segmentation fault를 유발했다면, 전역변수를 1로 설정하는 커스텀 핸들러가 동작했을테고, 이후의 if문을 정상적으로 통과할 수 있다.
==> 공격자가 ptrace에 bp걸고 PT_DENY_ATTACH 인자를 변경하여 디버깅이 가능하도록 한 경우라면, 두번째 ptrace attach가 제대로 동작하면서 전역변수가 그대로 0(false)로 유지되므로 if문을 통과하지 못하고 공격자의 디버깅 상태를 감지할 수 있게된다.
[우회 방법]
1. 첫번째 ptrace 우회
공격자가 디버거로 attach를 할 수 있어야하니까 PT_DENY_ATTACH를 먼저 우회함.
2. signal에 중단점 설정하고 핸들러 주소를 알아둠.
3. 두번째 ptrace 우회
4. 수동으로 signal 핸들러를 호출한다.
if문 검사 전에 전역변수 값을 바꾸려는 것인데, 여러 순간에 검사하는 게 아니라면 그냥 if문을 우회하는 것으로도 충분.
물론 전역변수 메모리 값을 직접 바꿔도 됨. (핸들러 함수 분석 결과 전역변수 값 하나만 바꾼다는 확신이 있다면 ㄱㄱ)
우회는 알아서...
'iOS 앱 점검(ObjC) > 안티디버깅' 카테고리의 다른 글
ios) 안티디버깅 - getppid() (0) | 2020.07.27 |
---|---|
ios) 안티디버깅 - sysctl (0) | 2020.07.27 |