一枚酸心果子

果子果子果子果子果子~~~

Frida特征检测及对应的绕过技术解析

I. Frida检测机制概述

1. 为什么要检测Frida

a. 安全威胁分析

  • 动态分析风险:Frida可以实时修改应用行为,绕过安全机制
  • 数据泄露风险:通过Hook可以获取敏感数据和API调用
  • 逆向工程风险:Frida使得应用逻辑完全暴露给攻击者

b. 检测目标

  • 防止调试:阻止安全研究人员分析应用
  • 保护知识产权:防止核心算法被逆向
  • 合规要求:满足金融、支付等行业的合规标准

2. Frida检测分类

a. 按检测层次分类

  • Java层检测:检测Frida Java API调用
  • Native层检测:检测Frida Native Hook
  • 系统层检测:检测Frida进程和文件

b. 按检测方式分类

  • 静态检测:检测Frida相关文件和特征
  • 动态检测:运行时检测Frida行为
  • 混合检测:结合多种检测方式

II. Frida常见检测特征

1. 进程和文件检测

进程和文件检测是最基础的Frida检测方式,通过检查系统中是否存在Frida相关的进程和文件来判断是否被Hook。这种检测方式简单直接,但容易被绕过。

a. Frida进程检测

Frida进程检测通过执行ps命令来查看当前运行的进程,寻找包含”frida”或”gadget”关键字的进程。这是最常见的检测方式之一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 检测Frida进程
public boolean detectFridaProcess() {
try {
Process process = Runtime.getRuntime().exec("ps");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("frida") || line.contains("gadget")) {
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

b. Frida文件检测

Frida文件检测通过检查特定路径下是否存在Frida相关的文件,如frida-server、gadget等。这些文件通常位于/data/local/tmp//system/lib/目录下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 检测Frida相关文件
public boolean detectFridaFiles() {
String[] fridaFiles = {
"/data/local/tmp/frida-server",
"/data/local/tmp/gadget",
"/system/lib/libfrida-gadget.so",
"/system/lib64/libfrida-gadget.so"
};

for (String file : fridaFiles) {
if (new File(file).exists()) {
return true;
}
}
return false;
}

2. 端口和网络检测

端口和网络检测通过检查Frida默认端口27042是否被占用,以及网络连接中是否存在可疑的Frida通信。这种检测方式相对隐蔽,但可以通过端口随机化来绕过。

a. Frida端口检测

Frida默认使用27042端口进行通信,检测程序会尝试连接这个端口来判断是否有Frida在运行。

1
2
3
4
5
6
7
8
9
10
11
// 检测Frida默认端口
public boolean detectFridaPort() {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress("127.0.0.1", 27042), 1000);
socket.close();
return true;
} catch (Exception e) {
return false;
}
}

b. 网络连接检测

网络连接检测通过执行netstat命令来查看当前网络连接,寻找包含Frida端口或相关关键字的连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 检测可疑网络连接
public boolean detectSuspiciousConnections() {
try {
Process process = Runtime.getRuntime().exec("netstat -an");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("27042") || line.contains("frida")) {
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

3. Hook检测

Hook检测是检测Frida最直接的方式,通过检查关键方法是否被Hook,以及异常堆栈中是否包含Frida相关信息来判断。这种检测方式精度较高,但需要深入了解Hook机制。

a. Java层Hook检测

Java层Hook检测通过检查关键方法的声明类是否被修改,以及异常堆栈中是否包含Frida相关的类名来判断是否被Hook。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 检测Java层Hook
public boolean detectJavaHook() {
try {
// 检测关键方法是否被Hook
Method method = String.class.getMethod("equals", Object.class);
if (method.getDeclaringClass() != String.class) {
return true;
}

// 检测异常处理
try {
throw new RuntimeException("test");
} catch (RuntimeException e) {
StackTraceElement[] stack = e.getStackTrace();
for (StackTraceElement element : stack) {
if (element.getClassName().contains("frida")) {
return true;
}
}
}
} catch (Exception e) {
return true;
}
return false;
}

b. Native层Hook检测

Native层Hook检测通过检查关键函数的入口点是否被修改,通常Hook会在函数开头插入JMP指令来跳转到Hook函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Native层Hook检测
bool detectNativeHook() {
// 检测关键函数是否被Hook
void* target_func = dlsym(RTLD_DEFAULT, "strlen");
if (target_func == nullptr) {
return true;
}

// 检测函数入口点
uint8_t* func_start = (uint8_t*)target_func;
if (func_start[0] == 0xE9 || func_start[0] == 0xEB) { // JMP指令
return true;
}

return false;
}

4. 线程检测

线程检测通过检查当前运行的线程中是否存在Frida相关的线程名,以及线程数量是否异常来判断是否被Hook。Frida会创建多个工作线程来处理Hook逻辑。

a. Frida线程检测

Frida会创建多个线程来处理不同的任务,如gum-js-loop线程用于执行JavaScript代码,检测程序会枚举所有线程并检查线程名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 检测Frida相关线程
public boolean detectFridaThreads() {
try {
Thread[] threads = new Thread[Thread.activeCount()];
Thread.enumerate(threads);

for (Thread thread : threads) {
if (thread != null) {
String threadName = thread.getName();
if (threadName.contains("frida") ||
threadName.contains("gadget") ||
threadName.contains("gum-js-loop")) {
return true;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

b. 线程数量异常检测

正常应用的线程数量通常在10-50个之间,如果线程数量异常增多,可能存在Frida等调试工具在运行。

1
2
3
4
5
6
7
8
9
// 检测线程数量异常
public boolean detectThreadCountAnomaly() {
int threadCount = Thread.activeCount();
// 正常应用线程数通常在10-50之间
if (threadCount > 100) {
return true; // 可能存在Frida线程
}
return false;
}

5. 文件描述符检测

文件描述符检测通过检查进程的文件描述符中是否存在Frida相关的文件或网络连接。这种检测方式相对隐蔽,但可以通过Hook文件系统调用来绕过。

a. fd检测

通过读取/proc/self/fd目录来查看当前进程打开的所有文件描述符,寻找Frida相关的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 检测文件描述符
public boolean detectFridaFD() {
try {
Process process = Runtime.getRuntime().exec("ls -la /proc/self/fd");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("frida") || line.contains("gadget")) {
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

b. 网络连接fd检测

通过lsof命令查看当前进程的网络连接,检查是否存在连接到Frida端口的TCP连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 检测网络连接文件描述符
public boolean detectNetworkFD() {
try {
Process process = Runtime.getRuntime().exec("lsof -p " + android.os.Process.myPid());
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("TCP") && line.contains("27042")) {
return true; // 检测到Frida端口连接
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

6. Trace状态检查

Trace状态检查通过检查进程的调试状态来判断是否被调试器附加。这是检测Frida最直接的方式之一,因为Frida本质上就是一个调试器。

a. Trace检测

通过读取/proc/self/status文件中的TracerPid字段来判断是否有调试器在跟踪当前进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 检测Trace状态
public boolean detectTraceStatus() {
try {
// 检测ptrace状态
Process process = Runtime.getRuntime().exec("cat /proc/self/status");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("TracerPid:")) {
String tracerPid = line.split(":")[1].trim();
if (!tracerPid.equals("0")) {
return true; // 被调试器跟踪
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

b. 调试器附加检测

通过检查/proc/self/wchan文件来判断当前进程是否在等待ptrace相关的系统调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 检测调试器附加
public boolean detectDebuggerAttach() {
try {
// 使用ptrace检测
Process process = Runtime.getRuntime().exec("cat /proc/self/wchan");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String wchan = reader.readLine();
if (wchan != null && wchan.contains("ptrace")) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

7. 内存检测

内存检测通过检查内存映射和内存完整性来判断是否被Hook。这种检测方式相对复杂,但检测精度较高。

a. 内存映射检测

通过读取/proc/self/maps文件来查看当前进程的内存映射,寻找Frida相关的内存区域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 检测内存映射
public boolean detectMemoryMapping() {
try {
Process process = Runtime.getRuntime().exec("cat /proc/self/maps");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("frida") || line.contains("gadget")) {
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

b. 内存完整性检测

通过计算关键代码段的哈希值来判断代码是否被修改,这是检测Hook最直接的方式。

1
2
3
4
5
6
7
8
9
// 内存完整性检测
bool detectMemoryIntegrity() {
// 检测关键代码段是否被修改
uint8_t* code_start = (uint8_t*)0x400000; // 假设的代码段地址
uint32_t expected_hash = calculateHash(code_start, 1024);
uint32_t current_hash = calculateHash(code_start, 1024);

return expected_hash != current_hash;
}

III. Frida绕过方法详解

1. 进程和文件绕过

进程和文件绕过主要通过伪装进程名、隐藏文件路径和动态加载等方式来避免被检测。这些方法相对简单,但需要Root权限支持。

a. 进程名伪装

通过修改Frida进程的名称来避免被进程检测发现,这是最基础的绕过方式。

1
2
# 修改Frida进程名
frida-server -D -l script.js --name "com.example.app"

b. 文件路径绕过

通过将Frida文件放置在非标准路径下,或者使用应用私有目录来避免被文件检测发现。

1
2
# 使用非标准路径
frida_server_path = "/data/data/com.example.app/lib/libfrida.so"

c. 动态加载绕过

通过动态加载Frida代码来避免静态文件检测,这种方式更加隐蔽,但实现复杂度较高。

1
2
3
4
5
6
7
8
9
10
11
# 动态加载Frida
import frida

def load_frida_dynamically():
# 从内存中加载Frida
frida_code = """
Java.perform(function() {
// Frida代码
});
"""
return frida_code

2. 端口和网络绕过

端口和网络绕过主要通过端口随机化、网络代理和本地通信等方式来避免被检测。这些方法可以有效绕过端口检测,但需要修改Frida的通信方式。

a. 端口随机化

通过使用随机端口来避免被默认端口检测发现,这是最常用的绕过方式。

1
2
3
4
5
6
7
8
9
10
# 随机端口绕过
import random

def get_random_port():
return random.randint(30000, 60000)

# 使用随机端口
device = frida.get_usb_device()
session = device.attach("com.example.app")
script = session.create_script(js_code)

b. 网络代理绕过

通过代理服务器来转发Frida通信,避免直接连接被检测。

1
2
# 通过代理连接
device = frida.get_device_manager().add_remote_device("192.168.1.100:27042")

c. 本地通信绕过

通过文件系统或其他本地通信方式来避免网络检测,这种方式更加隐蔽。

1
2
3
4
5
# 使用本地通信
def local_communication():
# 通过文件系统通信
with open("/data/local/tmp/frida_comm", "w") as f:
f.write("command")

3. Hook检测绕过

Hook检测绕过主要通过Hook检测函数本身来返回虚假结果,这是最有效的绕过方式,但需要深入了解检测机制。

a. Java层Hook绕过

通过Hook Java层的检测函数来返回虚假结果,避免被Hook检测发现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 绕过Java层Hook检测
Java.perform(function() {
// Hook检测函数
var Process = Java.use("java.lang.Process");
Process.exec.overload("java.lang.String").implementation = function(cmd) {
if (cmd.includes("ps") || cmd.includes("frida")) {
return null; // 返回空结果
}
return this.exec(cmd);
};

// Hook文件检测
var File = Java.use("java.io.File");
File.exists.implementation = function() {
var path = this.getAbsolutePath();
if (path.includes("frida") || path.includes("gadget")) {
return false; // 返回不存在
}
return this.exists();
};
});

b. Native层Hook绕过

通过恢复原始函数或使用更隐蔽的Hook方式来避免被Native层检测发现。

1
2
3
4
5
6
7
8
9
// Native层Hook绕过
void bypassNativeDetection() {
// 恢复原始函数
void* original_func = get_original_function();
memcpy(hooked_func, original_func, 5); // 恢复前5字节

// 使用更隐蔽的Hook方式
install_inline_hook(target_func, hook_func);
}

4. 线程检测绕过

线程检测绕过主要通过伪装线程名和控制线程数量来避免被检测。这些方法相对简单,但需要Hook线程相关的API。

a. 线程名伪装

通过Hook线程创建和枚举函数来伪装线程名,避免被线程检测发现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 绕过线程检测
Java.perform(function() {
// Hook线程创建
var Thread = Java.use("java.lang.Thread");
Thread.$init.overload("java.lang.String").implementation = function(name) {
// 伪装线程名
if (name.includes("frida") || name.includes("gadget")) {
name = "system-thread-" + Math.random();
}
return this.$init(name);
};

// Hook线程枚举
var ThreadGroup = Java.use("java.lang.ThreadGroup");
ThreadGroup.enumerate.implementation = function(threads) {
var result = this.enumerate(threads);
// 过滤掉Frida相关线程
for (var i = 0; i < threads.length; i++) {
if (threads[i] && threads[i].getName().includes("frida")) {
threads[i] = null;
}
}
return result;
};
});

b. 线程数量控制

通过Hook线程计数函数来返回正常的线程数量,避免被线程数量异常检测发现。

1
2
3
4
5
6
7
8
9
10
11
12
// 控制线程数量
Java.perform(function() {
var Thread = Java.use("java.lang.Thread");
Thread.activeCount.implementation = function() {
var count = this.activeCount();
// 限制线程数量在正常范围内
if (count > 50) {
return 30; // 返回正常线程数
}
return count;
};
});

5. 文件描述符检测绕过

文件描述符检测绕过主要通过Hook文件系统调用来过滤Frida相关的内容,这是绕过fd检测的主要方式。

a. fd检测绕过

通过Hook文件描述符读取函数来过滤Frida相关内容,避免被fd检测发现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 绕过fd检测
Java.perform(function() {
// Hook文件描述符读取
var FileInputStream = Java.use("java.io.FileInputStream");
FileInputStream.read.overload("[B").implementation = function(buffer) {
var result = this.read(buffer);
var content = Java.array('byte', buffer);
var str = String.fromCharCode.apply(null, content);

if (str.includes("frida") || str.includes("gadget")) {
// 过滤掉Frida相关内容
return 0;
}
return result;
};

// Hook lsof命令
var Runtime = Java.use("java.lang.Runtime");
Runtime.exec.overload("java.lang.String").implementation = function(cmd) {
if (cmd.includes("lsof") && cmd.includes("frida")) {
// 返回空结果
return null;
}
return this.exec(cmd);
};
});

b. 网络连接fd绕过

通过Hook网络连接相关函数来伪装网络连接,避免被网络连接fd检测发现。

1
2
3
4
5
6
7
8
9
10
// 绕过网络连接fd检测
Java.perform(function() {
// Hook网络连接检测
var Socket = Java.use("java.net.Socket");
Socket.getInputStream.implementation = function() {
var inputStream = this.getInputStream();
// 伪装网络连接
return inputStream;
};
});

6. Trace状态检测绕过

Trace状态检测绕过主要通过Hook状态文件读取函数来修改调试状态信息,这是绕过Trace检测的主要方式。

a. Trace状态绕过

通过Hook状态文件读取函数来修改TracerPid和wchan信息,避免被Trace状态检测发现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 绕过Trace状态检测
Java.perform(function() {
// Hook状态文件读取
var FileInputStream = Java.use("java.io.FileInputStream");
FileInputStream.read.overload("[B").implementation = function(buffer) {
var result = this.read(buffer);
var content = Java.array('byte', buffer);
var str = String.fromCharCode.apply(null, content);

if (str.includes("TracerPid:")) {
// 修改TracerPid为0
var modifiedContent = str.replace(/TracerPid:\s*\d+/, "TracerPid: 0");
var modifiedBytes = Java.array('byte', modifiedContent.split('').map(function(c) {
return c.charCodeAt(0);
}));
buffer.length = modifiedBytes.length;
for (var i = 0; i < modifiedBytes.length; i++) {
buffer[i] = modifiedBytes[i];
}
return modifiedBytes.length;
}
return result;
};

// Hook wchan检测
var FileInputStream = Java.use("java.io.FileInputStream");
FileInputStream.read.overload("[B").implementation = function(buffer) {
var result = this.read(buffer);
var content = Java.array('byte', buffer);
var str = String.fromCharCode.apply(null, content);

if (str.includes("ptrace")) {
// 替换ptrace相关内容
var modifiedContent = str.replace("ptrace", "sys_epoll_wait");
var modifiedBytes = Java.array('byte', modifiedContent.split('').map(function(c) {
return c.charCodeAt(0);
}));
buffer.length = modifiedBytes.length;
for (var i = 0; i < modifiedBytes.length; i++) {
buffer[i] = modifiedBytes[i];
}
return modifiedBytes.length;
}
return result;
};
});

b. 调试器附加绕过

通过Hook调试器检测函数来返回虚假结果,避免被调试器附加检测发现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 绕过调试器附加检测
Java.perform(function() {
// Hook调试器检测函数
var Debug = Java.use("android.os.Debug");
Debug.isDebuggerConnected.implementation = function() {
return false; // 始终返回false
};

// Hook进程状态检测
var Process = Java.use("android.os.Process");
Process.isDebuggerAttached.implementation = function() {
return false;
};
});

7. 内存检测绕过

内存检测绕过主要通过Hook内存读取函数来过滤Frida相关内容,这是绕过内存检测的主要方式。

a. 内存映射绕过

通过Hook内存映射读取函数来过滤Frida相关内容,避免被内存映射检测发现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 绕过内存映射检测
Java.perform(function() {
// Hook内存映射读取
var FileInputStream = Java.use("java.io.FileInputStream");
FileInputStream.read.overload("[B").implementation = function(buffer) {
var result = this.read(buffer);
var content = Java.array('byte', buffer);
var str = String.fromCharCode.apply(null, content);

if (str.includes("frida") || str.includes("gadget")) {
// 过滤掉Frida相关内容
return 0;
}
return result;
};
});

b. 内存完整性绕过

通过动态修改检测逻辑和使用内存保护来避免被内存完整性检测发现。

1
2
3
4
5
6
7
8
// 内存完整性绕过
void bypassMemoryIntegrity() {
// 动态修改检测逻辑
patch_detection_function();

// 使用内存保护
protect_memory_region();
}

IV. 进阶绕过技术

进阶绕过技术主要针对更复杂的检测机制,包括反反调试技术和动态对抗技术。需要更深入的系统知识和更复杂的实现方式。

1. 反反调试技术

反反调试技术主要针对应用的反调试机制,通过Hook调试器检测函数来绕过反调试保护。需要深入了解Android系统的调试机制。

a. 调试器检测绕过

通过HookAndroid系统提供的调试器检测函数来返回虚假结果,避免被反调试机制发现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 绕过调试器检测
Java.perform(function() {
// Hook调试器检测
var Debug = Java.use("android.os.Debug");
Debug.isDebuggerConnected.implementation = function() {
return false; // 始终返回false
};

// Hook调试器附加检测
var Process = Java.use("android.os.Process");
Process.isDebuggerAttached.implementation = function() {
return false;
};
});

b. 时间检测绕过

通过Hook时间相关函数来模拟正常的时间间隔,避免被基于时间差的反调试机制发现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 绕过时间检测
Java.perform(function() {
// Hook时间相关函数
var System = Java.use("java.lang.System");
var originalTime = System.currentTimeMillis;
var lastTime = originalTime.call(System);

System.currentTimeMillis.implementation = function() {
var currentTime = originalTime.call(System);
if (currentTime - lastTime < 100) { // 检测到时间异常
lastTime += 100; // 增加时间间隔
} else {
lastTime = currentTime;
}
return lastTime;
};
});

2. 动态对抗技术

动态对抗技术主要针对应用在运行时的动态检测机制,通过检测Hook并动态调整行为来避免被发现。需要实时监控和动态响应。

a. 动态Hook检测

通过检测Hook并动态调整行为来避免被动态检测机制发现,这是一种高级的对抗技术。

1
2
3
4
5
6
7
8
9
10
11
12
// 动态Hook检测
Java.perform(function() {
// 检测Hook并动态调整
var Thread = Java.use("java.lang.Thread");
Thread.sleep.implementation = function(millis) {
// 检测到Hook,调整行为
if (millis < 1000) {
millis = 1000; // 增加睡眠时间
}
return this.sleep(millis);
};
});

b. 行为模拟

通过模拟正常应用的行为来避免被行为分析检测发现,这是一种更加隐蔽的对抗技术。

1
2
3
4
5
6
7
8
9
10
11
// 模拟正常应用行为
Java.perform(function() {
// 模拟正常的网络请求
var URL = Java.use("java.net.URL");
URL.openConnection.implementation = function() {
// 添加正常的请求头
var connection = this.openConnection();
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
return connection;
};
});

V. 检测与绕过流程图

检测与绕过流程图展示了Frida检测与绕过的完整攻防流程,包括各种检测方式和对应的绕过技术。通过这个流程图可以清晰地理解攻防对抗的全貌。

flowchart TD
    A[应用启动] --> B{检测Frida}
    B --> C[进程检测]
    B --> D[文件检测]
    B --> E[端口检测]
    B --> F[Hook检测]
    B --> G[线程检测]
    B --> H[fd检测]
    B --> I[Trace状态检查]
    B --> J[内存检测]
    
    C --> K{检测到Frida?}
    D --> K
    E --> K
    F --> K
    G --> K
    H --> K
    I --> K
    J --> K
    
    K -->|是| L[触发防护机制]
    K -->|否| M[正常运行]
    
    L --> N[退出应用]
    L --> O[显示警告]
    L --> P[限制功能]
    
    Q[绕过技术] --> R[进程伪装]
    Q --> S[文件隐藏]
    Q --> T[端口随机]
    Q --> U[Hook绕过]
    Q --> V[线程伪装]
    Q --> W[fd隐藏]
    Q --> X[Trace绕过]
    Q --> Y[内存保护]
    
    R --> Z[绕过成功]
    S --> Z
    T --> Z
    U --> Z
    V --> Z
    W --> Z
    X --> Z
    Y --> Z
    
    Z --> M

VI. 自编译Frida与开源方案

自编译Frida和开源方案是绕过Frida检测的重要技术手段,通过修改Frida源码或使用开源替代方案来避免被检测。这些方案需要一定的技术基础,但绕过效果显著。

1. 自编译Frida

自编译Frida通过修改Frida源码来改变其特征,避免被检测发现。这是最有效的绕过方式之一,但需要深入了解Frida的架构。

a. 简单列举一些源码修改的要点

自编译Frida需要修改的关键点包括进程名、端口号、文件路径、线程名等特征信息。

1
2
3
4
5
6
7
8
9
# 修改Frida源码中的特征
# 1. 修改默认端口
sed -i 's/27042/12345/g' frida-core/src/frida-core.vala

# 2. 修改进程名
sed -i 's/frida-server/myapp/g' frida-core/src/frida-server.vala

# 3. 修改线程名
sed -i 's/gum-js-loop/system-loop/g' gum/gumjs/gumscriptbackend.cpp

b. 编译配置

编译时需要配置特定的编译选项来优化绕过效果。

1
2
3
4
# 编译配置
./configure --enable-static --disable-shared --with-pic
make -j4
make install

2. 开源绕过方案

网络上存在多种开源的Frida绕过方案,这些方案通常针对特定的检测机制设计,具有很好的实用性。

a. Frida-Anti-Detection

Frida-Anti-Detection是一个专门用于绕过Frida检测的开源项目,集成了多种绕过技术。

1
2
3
4
# 安装Frida-Anti-Detection
git clone https://github.com/darvincisec/Frida-Anti-Detection.git
cd Frida-Anti-Detection
pip install -r requirements.txt

b. Frida-Hook-Libart

Frida-Hook-Libart专门针对Android系统的libart库进行Hook,绕过ART虚拟机的检测。

1
2
3
4
5
6
7
8
9
// 使用Frida-Hook-Libart
Java.perform(function() {
// Hook ART虚拟机检测函数
var ArtMethod = Java.use("art.ArtMethod");
ArtMethod.invoke.implementation = function(thread, args) {
// 绕过ART检测
return this.invoke(thread, args);
};
});

3. Frida衍生工具

除了官方Frida,还有许多基于Frida开发的衍生工具,这些工具在特定场景下具有更好的绕过效果。

a. Frida-Scripts

Frida-Scripts是一个收集了大量Frida脚本的开源项目,包含多种绕过脚本。

1
2
3
# 使用Frida-Scripts
git clone https://github.com/0xdea/frida-scripts.git
frida -U -f com.target.app -l frida-scripts/android/anti-frida.js

b. Frida-Gadget

Frida-Gadget是Frida的嵌入式版本,可以集成到应用中,避免外部检测。

1
2
3
4
5
6
7
8
9
10
11
12
// 集成Frida-Gadget
#include "frida-gadget.h"

int main() {
// 初始化Frida-Gadget
frida_gadget_init();

// 加载脚本
frida_gadget_load_script("bypass.js");

return 0;
}

c. Frida-Inject

Frida-Inject是一个轻量级的Frida注入工具,专门用于绕过检测。

1
2
3
4
5
6
7
# 使用Frida-Inject
from frida_inject import FridaInject

injector = FridaInject()
injector.attach("com.target.app")
injector.load_script("anti_detection.js")
injector.resume()

VII. 结语

在实际应用中,建议采用多层防护策略,结合静态检测、动态检测和行为分析,同时不断更新检测规则和绕过方法,形成良性的攻防对抗循环。同时也要注意合规使用,避免用于非法目的。

同类工具推荐:Xposed框架、Substrate、Cydia Substrate等Hook框架也面临类似的检测与绕过问题,可以借鉴Frida的检测绕过思路。

最后简单推荐几个开源的frida脚本集合
https://github.com/deathmemory/FridaContainer
https://github.com/dweinstein/awesome-frida
https://github.com/0xdea/frida-scripts

非果子开源,只是编写文章过程和实际使用过程中有参考到其中的内容

持续输出技术分享,您的支持将鼓励我继续创作!