모바일/보안

Frida : 안티디버깅 우회

parktest0325 2019. 8. 13. 19:27

ptrace

lib.so에서 export하고 리눅스, 유닉스 환경에서 프로세스 디버깅에 사용되는 함수이다. (gdb도 ptrace 이용함)

 

ptrace라는 이름의 26번 커널 시스템콜이 내부적으로 사용된다.

# cat /usr/include/asm/unistd.h | grep ptrace

#define __NR_ptrace 26  

 

ptrace(PTRACE_ATTACH, ... ); // process에 Attach해서 작업. Dettach 하기 전까진 타겟은 STOP 상태이다.

ptrace(PTRACE_GETREGS, ... ); // 대상 process의 레지스터 목록 가져오기 return 값은 

ptrace(PTRACE_PEEKDATA, ... ); // 데이터 쓰기 (레지스터 크기만큼씩 읽고 쓰기가 가능하다)

ptrace(PTRACE_POPEDATA, ... ); // 데이터 읽기

 

 

ptrace를 이용한 안티디버깅 예시

void anti_debug() {

    child_pid = fork();

    if (child_pid == 0)
    {
        int ppid = getppid();
        int status;

        if (ptrace(PTRACE_ATTACH, ppid, NULL, NULL) == 0)
        {
            waitpid(ppid, &status, 0);

            ptrace(PTRACE_CONT, ppid, NULL, NULL);

            while (waitpid(ppid, &status, 0)) {

                if (WIFSTOPPED(status)) {
                    ptrace(PTRACE_CONT, ppid, NULL, NULL);
                } else {
                    // Process has exited for some reason
                    _exit(0);
                }
            }
        }
    }
}

fork()로 자식프로세스를 생성하고 부모프로세스를 ptrace하여 점유하게되면 다른 프로세스에서 ptrace시도 시 이미 점유되어 attach가 불가능하다.

 

1. 프리다를 이용한 안티디버깅 우회

import sys
import frida
def on_message(message,data):
    print "[%s] -> %s" % (message, data)

PACKAGE_NAME = sys.argv[1]
jscode = """
/// Inject_Jscode
"""
    
try:
    device = frida.get_usb_device(timeout=10)
    pid = device.spawn([PACKAGE_NAME])  
    print("App is starting ... pid : {}".format(pid))
    process = device.attach(pid)
    device.resume(pid)
    script = process.create_script(jscode)
    script.on('message',on_message)
    print('[*] Running Frida')
    script.load()
    sys.stdin.read()
except Exception as e:
    print(e)

frida의 device.spawn을 이용해서 앱 실행 전에 프리다가 후킹을 시도하여 안티디버깅을 우회

 

또는

frida -U -f <Package_name> --no-pause  명령어를 이용

 

 

2. 안티디버깅 로직의 smali를 변조하여 리빌드

무결성 체크로직도 함께 우회해야 한다.