1. ptrace
ptrace는 여러 유닉스와 유닉스 계열 운영 체제에서 발견되는 시스템 호출이다. ptrace(이 이름은 "process trace"의 축약형이다)를 통해 컨트롤러가 대상의 내부 상태를 조사하고 조작하게 함으로써, 한 프로세스가 다른 프로세스를 제어할 수 있다. ptrace는 디버거와 다른 코드 분석, 특히 소프트웨어 개발을 도와주는 도구들에서 사용된다.
[출처] 위키백과 https://ko.wikipedia.org/wiki/Ptrace
2. 목적
본래의 목적은 안드로이드 LD_PRELOAD 환경 변수에 라이브러리명을 인젝션하여 특정 함수를 후킹하는 것
2-1) LD_PRELOAD
프로세스 실행 전 라이브러리 로딩 과정에서 해당 환경변수에 추가되어있는 라이브러리를 우선적으로 로딩하게 만드는 환경변수
2-2) 메인아이디어
zygote를 kill하게되면 init프로세스에서 zygote프로세스를 재생성하며, 이때 LD_PRELOAD환경 변수가 재로드되고, 이때 값을 조작하여 원하는 라이브러리를 인젝션한다.
2-3) 목적 달성 과정
- zygote process kill (성공)
- new zygote process의 pid를 알아냄 (성공)
- new zygote process에서 실행하는 execve 실행 시점에서 환경변수 값 조작 (실패)
- 후킹 여부 확인 (실패)
3. aarch32 vs aarch64
3-1) register갯수가 다름
- register갯수가 대폭 추가되면서 ptrace를 이용하여 system call을 이해하기 위해 사용되는 register번호 또한 달라졌으며, 그에 따라 코드에서 사용하는 구조체 및 상수 또한 달라졌다.
|
aarch32 |
aarch64 |
system call number표시 reg |
7 |
8 |
IP reg |
ARM_ip |
12 |
return 값 reg | 0 | 0 |
reg표시 structure | pt_regs | user_regs_struct |
syscall number 참조 | 리눅스 syscall number와 동일 | ndk-bundle\include\asm-generic\unistd.h |
- aarch64 레지스터 설명
r30 (LR): The Link Register, is used as the subroutine link register (LR) and stores the return address when Branch with Link operations are performed.
r29 (FP): The Frame Pointer
r19…r28: Callee-saved registers
r18: The Platform Register, if needed; otherwise a temporary register.
r17 (IP1): The second intra-procedure-call temporary register (can be used by call veneers and PLT code); at other times may be used as a temporary register.
r16 (IP0): The first intra-procedure-call scratch register (can be used by call veneers and PLT code); at other times may be used as a temporary register.
r9…r15: Temporary registers
r8: Indirect result location register
r0…r7: Parameter/result registers
3-2) zygote 프로세스
- aarch32에서는 zygote process밖에 존재 하지 않지만, aarch64에서는 zygote / zygote64 process가 존재함
- aarch64에서 zygote 프로세스는 두 개지만 zygote process를 kill하면 zygote64 또한 재 실행되는 것으로 보임
3-3) zygote 프로세스 kill 후
- 새 프로세스 생성 전 aarch32에서는 stat64 system call을 사용하여 실행 바이너리(app_process)의 정보를 얻어 오지만, aarch64에서는 fstat64 system call을 사용하여 실행 바이너리(app_process32 / app_process64)의 정보를 얻어오며, aarch64의 app_process 바이너리는 app_process64의 심볼릭 링크이다.
- aarch32에서는 fork system call로 zygote process를 생성하지만 aarch64에서는 clone system call을 이용
- 개인적으로 fork / clone system call의 가장 큰 차이점은 프로세스 복사(생성) 이 후 실행 할 바이너리를 경로로 넘겨주느냐(fork), file descriptor(clone)의 차이가 가장 큰 외적 차이로 보이며, 조사를 통한 내적 차이는 parent process로부터 메모리 스택을 공유(clone) / 비공유(fork)라고 생각하고, 전반적으로 clone의 system call이 fork에 비해 비교적 비용이 낮은 것으로 설명이 되어있었다.
3-4) execve system call 탐지 가능 여부
이 부분은 aarch64에서도 아마도(?) 탐지가 가능할 것이나 내 능력/자료가 부족한 것으로 생각된다.
- ptrace를 사용하여 레지스터 값들을 조사할 때 실행되는 system call number에 execve system call에 해당하는 220번이 탐지 되지 않음
- strace를 이용하여 값을 조사했을 때는 execve system call이 실행되는 것이 확인됨
4. aarch64에서 ptrace사용
4-1) 레지스터 값을 가져오기 위한 구조체 선언
1 2 3 4 5 6 7 8 9 10 | //#include <linux/PTRACE.h> //#include <sys/PTRACE.h> //aarch64에서 달라진 구조체 struct user_regs_struct gregs; //aarch64에서 추가로 필요한 부분 struct iovec iovecs; iovecs.iov_base = &gregs; iovecs.iov_len = sizeof(gregs); |
4-2) ptrace attatch
1 2 3 4 5 6 7 8 | // step 1. init프로세스에 PTRACE attatch ret = PTRACE(PTRACE_ATTACH, INIT_PID, (void *)1, 0); if(ERROR == ret){ printf("[ERROR] PTRACE_ATTACH failed return = %d\n", ret); } else{ printf("[INFO] PTRACE_ATTACH SUCCESS\n"); } |
4-3) 레지스터 값 얻어오기
1 2 3 4 5 6 7 8 9 10 11 | // cpu 레지스터 획득 ret = PTRACE(PTRACE_GETREGSET, INIT_PID, (void *)NT_PRSTATUS, &iovecs); if ( ret < 0 ) { printf("[ERROR] PTRACE_GETREGSET ERROR : %d\n", ret); } else{ printf("[INFO] PTRACE_GETREGSET SUCCESS : %d\n", ret); } |
4-4) system call 탐지
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #define SYSCALL_REGISTER(r) r.regs[8] #define RESULT_REGISTER(r) r.regs[0] #define IP_REGISTER(r) r.regs[12] //gregs.regs[28]이 0이 무엇을 뜻하는 지는 알수 없으나, //여러 번 반복해보는 과정을 통해 0인 경우일 때가 new pid가 리턴되는 것을 확인함 //그 중에서도 리턴 값이 정상인 것과 비정상인 값이 있었는데, //정상 값을 찾기 위해 maximum pid값을 지정함(cat /proc/sys/kernel/pid_max -> 32768) if ( 220 == SYSCALL_REGISTER(gregs) && 40 == IP_REGISTER(gregs) && 0 == gregs.regs[28] && 32768 >= RESULT_REGISTER(gregs) ) { printf("[*] Found a new zygote64 process: %ld\n", RESULT_REGISTER(gregs)); new_zygote_pid = RESULT_REGISTER(gregs); } |
4-5) ptrace detach
1 2 3 4 5 6 7 | //ptarce detach if(PTRACE(PTRACE_DETACH, INIT_PID, (void*)1, 0) < 0) { printf("[ERROR] PTRACE_DETACH error\n"); } else{ printf("[INFO] PTRACE_DETACH SUCCESS\n"); } |
'Study > 안드로이드' 카테고리의 다른 글
JDB이용하여 앱 동적 분석하기 (0) | 2017.02.13 |
---|---|
Drozer 기본 명령어 (0) | 2017.02.09 |
안드로이드 재서명 하기 (2) | 2016.05.08 |
안드로이드 apk서명 (0) | 2016.05.02 |
안드로이드 apk꺼내기 (0) | 2016.05.02 |