개요
Frida는 앱 실행 중에 동적으로 함수를 후킹할 수 있고, 메모리를 조작할 수 있기 때문에 개발자가 의도하지 않은 방식으로 앱을 동작시킬 수 있다.
따라서 Frida를 허용하지 않는 앱에서는 Frida를 탐지하고 앱의 동작을 중지하는 Anti-Frida 기능을 포함하고 있는 경우가 많다.
필자가 개발한 TodoList 앱의 Frida 포트 검사 기능은 Frida에서 사용하는 포트 대역을 검사한다. 기기 내에서 실행되는 Frida 서버는 클라이언트와 통신하기 위해 네트워크 포트를 연다.
일반적인 안드로이드 환경에서는 거의 사용하지 않는 포트 대역을 사용하기 때문에 만약 해당 포트가 열려있다면 이는 Frida가 사용 중일 확률이 높다.
이번 포스팅에서는 TodoList앱의 포트 검사 기능의 소스 코드를 살펴보면서 Frida 탐지 기법 중 Frida가 사용하는 대역의 포트를 탐지하는 로직과, 이를 우회하는 스크립트를 구현해 보았다.
탐지 로직
checkPort 함수 분석
public boolean checkPort() {
for (int i = 26000; i < 27500; i++) { // 26000~27499 포트 open 여부 검사
try {
Socket s = new Socket("127.0.0.1", i);
if (s.isConnected()) {
return true;
}
} catch (IOException ignore) {
}
}
return false;
}
- checkPort 함수는 Socket 객체를 통해 26000부터 27499까지 총 1500개의 포트가 열려있는지 검사한다. (26000~27499 포트 : 일반적으로 기기 내의 Frida 서버가 Frida 클라이언트와 통신하기 위해 주로 사용하는 포트)
- Frida의 기본 설정 포트는 27042 포트이며, 임의로 포트를 변경했을 가능성이 있기 때문에 보통 이에 근접한 포트 대역을 전부 검사한다.
- 이 함수는 반복적으로 여러 포트에 연결 을 시도하기 때문에 기기 환경에 따라 탐지 속도가 상이할 수 있다.
- 만약 해당 포트 중 열려있는 포트가 하나라도 존재한다면 Frida가 탐지되었다고 판단 하고 True를 반환한다.
- 로컬 기기에 26000부터 27499까지 총 1500개의 포트에 대하여 Socket 객체를 생성한다.
- isConnected 함수를 통해 열려있는 포트가 있는지 for문으로 확인한 후, 열린 포트가 있다면 Frida가 탐지되었다고 판단하고 True를 반환한다.
- 만약 열린 포트가 없다면 해당 포트에 Frida 연결이 탐지되지 않은 것으로 판단하고 False를 반환한다.
탐지 우회
환경 설정
Frida 탐지 기능을 우회하기 전에 실습을 위해 Frida 포트를 탐지한 상태가 되도록 만들어야 한다.
앞선 코드 분석 과정에서 26000부터 27499까지의 포트 중 하나라도 열려있으면 탐지된다는 사실을 확인했다.
따라서 해당 포트 중 하나를 임의로 열어 Frida 포트 검사 기능에서 탐지가 되도록 설정한다.
1. Frida 서버가 사용하는 포트 확인
generic_x86_64_arm64:/ # netstat -ntpl | grep frida
tcp 0 0 127.0.0.1:27042 0.0.0.0:* LISTEN 13478/frida-server-16.1.3-android-x86_64
tcp 0 0 127.0.0.1:27042 127.0.0.1:40696 ESTABLISHED 13478/frida-server-16.1.3-android-x86_64
tcp6 0 0 :::43811 :::* LISTEN 13478/frida-server-16.1.3-android-x86_64
2. Frida 서버 실행 시 포트 지정
generic_x86_64_arm64:/data/local/tmp # ./frida-server-16.1.3-android-x86_64 -l 0.0.0.0:27000

Frida 포트 검사 우회
우회 스크립트
function modifyCheckPortRet() {
Java.perform(function() {
var FridaDetector = Java.use("com.example.todolist_for_frida.FridaDetector");
FridaDetector.checkPort.implementation = function() {
return false;
};
});
}
function modifySocketParameter() {
Java.perform(function() {
var Socket = Java.use("java.net.Socket");
var constructor = Socket.$init.overload('java.lang.String', 'int');
constructor.implementation = function(hostname, port) {
if (hostname == "127.0.0.1" && 26000 <= port && port < 27500) {
port = 1234;
}
return constructor.call(this, hostname, port);
};
});
}
modifyCheckPortRet();
modifySocketParameter();
checkPort 함수 반환값 조작
function modifyCheckPortRet() {
Java.perform(function() {
var FridaDetector = Java.use("com.example.todolist_for_frida.FridaDetector"); // 제공받은 Wrapper를 통해 클래스 내의 checkPort 함수에 접근
FridaDetector.checkPort.implementation = function() {
return false;
};
});
}
- 먼저 Java.use 함수를 사용해 Frida 내에서 FridaDetector클래스에 접근할 수 있도록 Wrapper를 제공받아 Wrapper를 통해 클래스 내의 checkPort 함수에 접근한다.
- 이 때 implementation을 사용하면 Frida 스크립트로 구현한 함수로 해당 함수를 덮을 수 있다.
- 따라서 함수의 반환값이 항상 False인 함수를 구현하여 checkPort 함수를 우회한다.
Socket 생성자 파라미터 변조
checkPort라는 함수명을 알 수 없는 난독화(Obfuscation) 환경이나, 탐지 로직이 네이티브 라이브러리(.so) 내부 혹은 시스템 API 수준에서 직접 호출될 경우를 가정한다.
이럴 때는 앱 내부 함수를 찾기 어렵기 때문에, 자바 표준 API인 java.net.Socket 자체를 후킹하여 탐지 시도 자체를 무력화하는 전천후 우회 기법을 함께 생각해 볼 수 있다.
function modifySocketParameter() {
Java.perform(function() {
var Socket = Java.use("java.net.Socket");
var constructor = Socket.$init.overload('java.lang.String', 'int'); // Socket 의 생성자 후킹. host와 port를 인자로 받는 생성자를 후킹한 것
constructor.implementation = function(hostname, port) {
if (hostname == "127.0.0.1" && 26000 <= port && port < 27500) {
port = 1234;
}
return constructor.call(this, hostname, port);
};
});
}
- Socket 클래스는 다른 엔드포인트와 통신하기 위한 소켓 클라이언트가 구현된 클래스다.
- 만약 앱이 Socket 을 이용해 127.0.0.1 의 26000~ 27499 범위의 포트에 접속을 시도하는 요청이 발생한다면 이는 Frida 탐지를 위한 요청으로 판단할 수 있다.
-> 이 때 해당 클래스의 생성자를 후킹하여 다른 포트로 변경한다면 Frida 포트에 아예 접속하지 않도록 할 수 있다.
Frida 내에서 클래스의 생성자는 $init을 통해 접근할 수 있다.
- 따라서 $init의 implementation을 덮는 방식으로 생성자를 후킹할 수 있다.
Socket 클래스는 다양한 형태의 파라미터를 받을 수 있도록 생성자가 오버로딩으로 선언되어 있다.
스크립트 실행 결과
> frida -U --timeout 30 -f com.example.todolist_for_frida -l ./Bypass_Frida_checkPort.js
____
/ _ | Frida 16.1.3 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to Android Emulator 5554 (id=emulator-5554)
Spawned `com.example.todolist_for_frida`. Resuming main thread!
[Android Emulator 5554::com.example.todolist_for_frida ]->

취약점 대응 방안
- 네이티브 레이어(C/C++) 구현: Java 레벨의 소켓 검사는 Frida Hooking에 너무 취약하므로, 포트 검사 및 탐지 로직을 수성(Native) 영역에서 구현하고, 탐지 시 자바 API를 거치지 않고 exit() 등으로 프로세스를 즉시 종료시켜야 한다.
- 랜덤 포트 구동 기법 대응: 단순히 특정 포트 대역(26000~27500)만 검사하는 것은 frida-server -l 옵션으로 완전히 다른 포트를 지정했을 때 우회된다.
때문에 포트 검사 외에도 /proc/self/maps나 /proc/self/task 내의 Frida 스레드 명(gmain 등)을 검사하는 다중 방어 메커니즘이 필요함.
'Mobile App Hacking : Android > Frida 탐지 우회' 카테고리의 다른 글
| 모듈 검사 우회 (0) | 2026.05.27 |
|---|---|
| 경로 검사 우회 (0) | 2026.05.27 |