1. 개요
안드로이드 앱에서는 실행 중인 프로세스에 로드된 라이브러리를 확인하여 Frida 사용 여부를 탐지할 수 있다.
본 분석에서는 /proc/self/maps 파일을 기반으로 프로세스 메모리 내 로드된 모듈을 확인하고, 특정 문자열 존재 여부를 통해 Frida 실행 여부를 판단하는 로직을 분석하였다.
앱은 해당 파일의 내용을 문자열로 읽은 뒤 "frida" 문자열 포함 여부를 기준으로 탐지 결과를 결정한다.
2. 탐지 로직 분석
문서에 따르면 checkModule() 함수는 /proc/self/maps 파일을 읽어 전체 내용을 문자열로 구성한 뒤 "frida" 문자열 포함 여부를 검사한다.
동작 흐름은 다음과 같다.
- /proc/self/maps 파일 열기
- 파일 내용을 한 줄씩 읽어 문자열로 누적
- 전체 문자열에서 "frida" 포함 여부 확인
- 포함 시 true 반환 (탐지)
- 미포함 시 false 반환 (정상)
이 파일은 현재 프로세스에 로드된 공유 라이브러리(.so) 목록을 포함하고 있으며, Frida 사용 시 frida-agent-xxx.so와 같은 문자열이 포함되는 것이 일반적이다.
3. 구조적 특징
이 탐지 방식은 다음과 같은 특징을 가진다.
첫째, 운영체제 레벨 정보를 기반으로 한다.
파일 시스템이 아닌 프로세스 메모리 맵 정보를 사용하기 때문에 비교적 신뢰도가 높은 방식이다.
둘째, 문자열 기반 탐지 방식이다.
특정 라이브러리의 실제 동작 여부가 아니라, 문자열 포함 여부만으로 판단한다.
셋째, 전체 데이터에 대한 후처리 방식이다.
파일 전체를 읽은 뒤 문자열 검색을 수행하기 때문에 입력 데이터 전체가 탐지 기준에 영향을 준다.
이 구조는 “최종 반환값”과 “입력 데이터(파일 내용)” 두 지점에서 개입 가능성을 가진다.
4. 우회 전략
4.1 탐지 결과 조작
가장 직접적인 방법은 checkModule() 함수의 반환값을 고정하는 것이다.
이 함수가 항상 false를 반환하도록 제어하면, 실제로 Frida 관련 모듈이 로드되어 있어도 탐지되지 않은 것처럼 동작한다.
이 방식은 구현이 단순하지만, 탐지 로직 전체를 무력화하는 접근에 해당한다.
4.2 입력 데이터 변조 (메모리 맵 내용 조작)
보다 구조적인 우회는 /proc/self/maps 파일을 읽는 과정에 개입하는 방식이다.
문서에서는 BufferedReader.readLine()을 후킹하여, 읽어온 문자열에서 "frida"가 포함된 경우 이를 제거하는 방식이 제시되어 있다.
핵심 아이디어는 다음과 같다.
var line = readLine.call(this);
if (line.includes("frida")) {
line = "";
}
return line;
이 경우 앱은 정상적으로 파일을 읽고 문자열을 구성하지만,
실제 탐지 기준이 되는 "frida" 문자열이 제거된 상태로 처리된다.
결과적으로 탐지 로직은 정상적으로 동작하지만, 탐지 조건이 만족되지 않게 된다.
5. 우회 원리 정리
이 우회의 핵심은 탐지 로직이 신뢰하는 입력 데이터를 변조하는 데 있다.
앱은 /proc/self/maps 파일 내용을 기반으로 판단을 수행하지만,
실행 시점에서 해당 데이터가 조작되면 실제 환경과 무관하게 탐지 결과를 제어할 수 있다.
즉, 모듈을 제거하는 것이 아니라
모듈이 존재하지 않는 것처럼 보이도록 만드는 방식이다.
6. 한계 및 고려사항
이 방식은 몇 가지 한계를 가진다.
첫째, 전역 영향 가능성이 존재한다.
BufferedReader.readLine()과 같은 범용 함수에 대한 후킹은 다른 파일 읽기 로직에도 영향을 줄 수 있다. 문서에서도 이를 고려하여 /proc/self/maps 파일에 대해서만 제한적으로 개입하는 방식이 사용된다.
둘째, 개입 시점이 비교적 넓다.
파일 읽기 단계에서 개입하기 때문에, 탐지 로직 외의 데이터 처리 흐름에도 영향을 줄 수 있다.
셋째, 우회 방식 혼합 시 분석이 어려워진다.
문서에서는 반환값 조작과 입력 데이터 변조를 함께 제시하고 있으며, 두 방식을 동시에 적용할 경우 실제 우회 원인을 구분하기 어렵다.
7. 개선 방향
보다 정밀한 우회를 위해서는 특정 파일과 특정 조건에서만 개입하는 방식이 필요하다.
예를 들어 /proc/self/maps 파일을 읽는 경우에만 제한적으로 문자열을 변조하면,
다른 파일 처리 로직에 대한 영향을 최소화할 수 있다.
또한 반환값 조작 방식과 입력 데이터 변조 방식을 분리하여 실험하면,
단순 우회와 구조적 우회의 차이를 보다 명확하게 설명할 수 있다.
8. 결론
모듈 기반 Frida 탐지는 비교적 신뢰도가 높은 방식이지만, 구조적으로 우회 가능성이 존재한다.
이번 사례에서 확인할 수 있는 핵심은 다음과 같다.
- 프로세스 메모리 맵을 통해 로드된 모듈을 확인한다
- 특정 문자열 포함 여부를 기준으로 탐지한다
- 따라서 입력 데이터 또는 판단 결과를 제어하면 우회가 가능하다
즉, 이 탐지 방식은 실제 Frida 동작 여부를 직접 제어하는 것이 아니라,
메모리 상의 흔적을 기반으로 간접적으로 판단하는 구조를 가진다.
핵심 인사이트
모듈 기반 탐지는 “Frida가 실행 중인가”를 직접 확인하는 것이 아니라, “메모리 맵에 특정 문자열이 존재하는가”를 통해 간접적으로 판단하는 방식이다.
따라서 우회의 핵심은 모듈을 제거하는 것이 아니라, 탐지 기준이 되는 메모리 정보 자체를 제어하는 데 있다.