抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

基于网络的检测

检测 Frida 的监听端口

Frida 在运行时通常会开启特定的监听端口(默认 2704227043)。可以通过扫描本地端口来检测是否存在这些端口。

1
2
3
4
5
6
7
8
9
10
private boolean isFridaPortOpen() {
try {
Socket socket = new Socket("127.0.0.1", 27042);
socket.close();
return true; // Detected Frida
} catch (IOException e) {
// Port not open
}
return false;
}

检测网络流量中的 Frida 特征

  • 监控应用的网络流量,检测是否存在与 Frida 工具相关的流量特征。
  • 例如,检测与 localhost:27042 的连接

检测 D-Bus 协议通信

frida 使用 D-Bus 协议通信,这个通信协议并不常见

  1. Frida Server 的通信:
    • 如果 Frida Server 被配置为通过 D-Bus 进行通信,可以检测是否有 Frida Server 在特定的 D-Bus 总线上注册。
  2. D-Bus 信号监听或方法调用 Hook:
    • 检测是否有进程在监听特定的系统服务信号,或者通过 D-Bus 调用敏感接口。
  3. 可疑 D-Bus 服务注册:
    • 检测是否有可疑的 D-Bus 服务名或对象路径被注册(与 Frida 的模块或库有关的名称)。
  4. D-Bus 通信流量分析:
    • 检测 D-Bus 通信中是否存在与 Frida 相关的关键字或行为模式。

基于进程和文件的检测

检测 Frida 相关的进程

Frida 在运行时会启动特定的进程(如 frida-servergadget),这些进程通常可以通过以下方式检测到:

  • Android:
    • 使用 ps 命令或 /proc 文件系统扫描进程列表中是否存在 frida-server 或类似名称的进程。
    • 检测常见的 Frida 进程名称:
      • frida-server
      • gadget
      • libfrida-agent.so
      • frida-helper
  • iOS:
    • 检查是否存在 FridaGadget 或类似的动态库进程。

示例代码(Android):

通过遍历 /proc 文件夹下的所有进程目录(以 PID 为名称的文件夹)并读取每个进程的 cmdline 文件,可以获取当前所有进程的名称。

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
public class FridaDetection {

// 检测 Frida 相关进程的存在
public static boolean isFridaProcessDetected() {
// 可疑进程列表
String[] suspiciousProcesses = {"frida-server", "gadget", "libfrida-agent.so", "frida-helper"};

// 遍历 /proc 目录
File procDir = new File("/proc");
File[] files = procDir.listFiles();

if (files == null) {
return false;
}

for (File file : files) {
// 进程目录名称通常为 PID(数字)
if (file.isDirectory() && file.getName().matches("\\d+")) {
File cmdlineFile = new File(file, "cmdline");
try (BufferedReader reader = new BufferedReader(new FileReader(cmdlineFile))) {
String cmdline = reader.readLine();
if (cmdline != null) {
for (String suspicious : suspiciousProcesses) {
if (cmdline.contains(suspicious)) {
// 如果发现可疑进程,返回 true
System.out.println("Detected Frida process: " + cmdline);
return true;
}
}
}
} catch (Exception e) {
// 忽略无法读取的进程
}
}
}

// 未检测到可疑进程
return false;
}

扫描 task 目录

在 Linux 和 Android 系统中,每个进程都可以通过 /proc/[pid]/task 目录查看其线程信息。Frida 在注入目标进程时,可能会创建额外的线程用于执行其任务(例如动态注入、代码 Hook 或通信处理)。通过扫描 /proc/[pid]/task 目录下的线程信息,可以检测出与 Frida 相关的特征线程,从而判断 Frida 是否已注入目标进程。

原理:

  1. 线程命名特征
    • Frida 在注入进程后,可能会创建一些具有特定名称的线程(如 gum-js-loop, gum-js-worker 等)。
    • 这些线程名称会暴露在 /proc/[pid]/task/[tid]/comm 文件中(comm 文件记录线程的名称)。
  2. 线程数量异常
    • Frida 注入后,目标进程通常会多出额外的线程。这些线程的数量和行为可能与正常的线程模式不一致。
  3. 线程行为异常
    • Frida 创建的线程可能会执行特定的任务(如动态库加载、代码注入等),这些行为可以通过进一步的分析来发现。

通过扫描 task 目录下的线程的 comm 文件或 status 文件,看是否存在上述字符。

检测 Frida 安装文件

Frida 的运行通常需要一些特定的文件或库,可以通过检查文件系统是否存在这些文件来检测其存在:

  • Android 文件路径:
    • /data/local/tmp/frida-server
    • /data/local/tmp/gadget.so
    • /data/local/tmp/libfrida-agent.so
    • /dev/frida
  • iOS 文件路径:
    • /Library/FridaGadget.dylib
    • /usr/sbin/frida-server

检测文件示例(Android):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private boolean isFridaFilesPresent() {
String[] suspiciousFiles = {
"/data/local/tmp/frida-server",
"/data/local/tmp/libfrida-agent.so",
"/data/local/tmp/gadget.so",
"/dev/frida"
};
for (String file : suspiciousFiles) {
File f = new File(file);
if (f.exists()) {
return true; // Detected Frida files
}
}
return false;
}

基于动态库的检测

检测已加载的 Frida 动态库

在 Linux 和 Android 系统中,/proc/self/maps 文件是一个重要的内存映射文件,它记录了当前进程的内存布局信息,包括加载的动态链接库(.so/.dylib 文件)、匿名内存区域、堆栈区域等。

Frida 在目标进程中运行时会加载一些特定的动态库(如 libfrida-agent.solibfrida-gadget.so),这些库会出现在 /proc/self/maps 文件中。因此,通过扫描这个文件,可以有效检测 Frida 是否存在。

原理

Frida 在运行时会动态加载一些特定的库,可以通过检查进程的已加载库列表来检测这些库是否存在。

  • 常见的 Frida 库名称:
    • libfrida-gadget.so
    • frida-agent.so
    • libfrida-core.so

检测方法(Android):

  • 通过 /proc/self/maps 文件:
    • 读取当前进程的内存映射,查找是否有 Frida 的库被加载。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private boolean isFridaLibraryLoaded() {
try {
BufferedReader reader = new BufferedReader(new FileReader("/proc/self/maps"));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("frida") || line.contains("gadget")) {
return true; // Detected Frida library
}
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}

使用 dlopendlsym 检测 Frida 库

使用动态链接器的 API( dlopendlsym)在运行时检查特定的库是否被加载。

  1. 动态库检测
    • 当 Frida 注入目标进程时,会加载其核心动态库(如 libfrida-agent.solibfrida-gadget.so)。
    • 使用 dlopen 可以尝试加载这些动态库,如果它们已经被加载,则不会重复加载,并且会返回一个有效的句柄。
  2. 符号解析检测
    • Frida 的动态库中包含一些特定的符号(如 frida_agent_maingum_interceptor_attach 等)。
    • 使用 dlsym 可以检查这些符号是否存在,如果存在,则说明 Frida 的库已经被注入。
  3. 动态库路径特征
    • Frida 的动态库通常加载在 /data/local/tmp 或其他可疑路径中,可以通过动态库路径进行进一步验证。

示例(C 代码):

1
2
3
4
5
6
7
8
9
10
11
12
<C>#include <dlfcn.h>
#include <stdio.h>

int detect_frida() {
void *handle = dlopen("libfrida-core.so", RTLD_LAZY);
if (handle) {
printf("Frida detected!\n");
dlclose(handle);
return 1;
}
return 0;
}

inlinehook 检测:

看雪 inlinehook 检测文章

  1. 函数劫持
    • Frida 使用 gum_interceptor 等组件,通过修改函数的入口点指令,将原始函数的执行流程劫持到 Frida 的 Hook 函数中。
    • 通常,函数入口点的前几条指令(例如 x86 架构下的 jmpcall 指令)会被替换为跳转指令,指向 Frida 的代码。
  2. 函数指令被修改
    • 被 Hook 的函数,其入口指令(如 mov, jmp, call)会被替换为跳转到 Frida 插入的代码地址。
    • 例如:
      • 在 x86 架构中,可能是 jmp 0xdeadbeef
      • 在 ARM 架构中,可能是 b 0x12345678
  3. 内存保护特性
    • 通常情况下,代码段(如 .text 段)是只读的,如果发现代码段被修改,说明可能存在 Inline Hook。

检查函数入口点的指令是否被修改

通过读取目标函数的入口点指令,与正常的函数指令进行对比,判断是否存在被篡改的情况。

我们可以通过检测每个函数的开头是否有 0xd61f020058000050 这样的一段代码来判断进程是否被 frida 附加了。

自定义 Syscall 防 Hook

Frida 的 Hook 主要发生在用户态函数(如 libc 函数)上,而系统调用(Syscall)是直接由内核处理的,无法直接通过 Frida 在内核级别 Hook。因此,通过直接调用系统调用(而不是通过 libc 等用户态封装的函数)可以绕过 Frida 的 Hook。

自定义系统调用可以直接与内核交互,绕过用户态的 Hook。通过直接调用系统调用,可以检查某些关键函数是否被 Hook,并实现防御。

原理:

  1. 直接调用 Syscall
    • 使用 syscall 指令(Linux x86 和 x86_64 平台)或类似的机制(ARM 上的 svc 指令)直接调用内核的系统调用接口,而不经过 libc 函数。
    • 这样可以绕过用户态的 Hook(如 Frida 对 openread 等函数的劫持)。
  2. 对比检测
    • 同时调用 libc 函数和 Syscall,并对比返回结果。如果结果不一致,说明用户态函数可能已被 Hook。
    • 例如:调用 open 时,直接使用 syscall 调用内核的 open,并与 libc 的 open 返回值对比。
  3. 完整性校验
    • 通过直接调用系统调用,检查重要内存区域(如 .text 段)的完整性,检测是否存在 Inline Hook 或其他篡改行为。

参考:

https://github.com/qtfreet00/AntiFrida%E5%92%8Chttps://github.com/muellerberndt/frida-detection

基于行为分析的检测

检测系统调用行为

Frida 会劫持系统调用,可以通过监控系统调用行为的异常来检测 Frida。例如:

  • 检测常见函数(如 ptrace, mprotect, malloc 等)的调用频率和调用栈。

检测代码注入行为

Frida 的动态注入可能会修改应用进程的代码段,可以通过检测代码段的完整性来判断是否被修改。例如:

  • 验证代码段的哈希值是否与预期一致。

对抗性检测

Frida 作为用户层工具,通常依赖于 root 权限或越狱环境,因此可以通过检测设备的运行环境来间接检测 Frida:

  • 检测设备是否 Root(Android)或越狱(iOS)
  • 检测调试工具的存在,如 gdblldb

Frida 的持久

双进程守护

Frida 双进程守护 是一种对抗检测或增强 Frida 稳定性的方法,通过运行两个相互守护的进程,在一个进程被杀死(如由于检测或防护措施)时,另一个进程会迅速重启被杀的进程,从而保证 Frida 的持续运行。

简单来说,双进程守护是一种 进程自恢复机制,它通过两个进程相互监控,确保目标进程(如 frida-server 或其他注入进程)在受到干扰或被终止时能够重新拉起。

双进程守护的工作原理

双进程守护的核心思想是:

  1. 启动两个进程(例如,Process AProcess B),每个进程都负责监控对方的运行状态。
  2. 如果任意一个进程被终止,另一个进程会立即重新启动被终止的进程。
  3. 守护进程之间通常通过以下方式监控对方:
    • 进程存在检测:例如,通过定期检查对方的 PID 是否仍然存在。
    • 管道通信:例如,两个进程之间建立一个通信管道(如 pipesocket),如果管道的连接断开,说明对方进程异常退出。
  4. 重新拉起被杀死的进程,确保两者始终在运行。

对抗双进程守护的方法

虽然双进程守护可以有效增强 Frida 的运行稳定性,但它也并非无法对抗。以下是常见的对抗方法:

  1. 批量杀死相关进程

    • 使用脚本同时杀死两个守护进程。例如,直接通过 pkill 命令杀死所有与 Frida 相关的进程:

      1
      pkill -f frida
  2. 进程组杀死

    • 确保杀死整个进程组,而非单一进程。
  3. 检测并删除守护逻辑

    • 分析守护进程的逻辑,找到其启动点(如脚本文件或守护代码),并删除或禁用它。
  4. 资源隔离

    • 使用沙箱环境隔离进程,阻止 Frida 守护进程相互监控。

frida 常见过检测思路:

1.自编译 frida server 魔改一些常见特征,然后过检测,例如 hluda-server

2.通过 hook strstr、strcmp 等系统函数,将一些比较 frida 的情况给 hook

通过 Hook 系统函数(如 strstr、strcmp),拦截对 Frida 特征的检测逻辑。

例如,当检测逻辑使用 strstr(line, "frida") 时,返回 NULL,使检测代码认为没有发现 Frida。

3.通过 hook readlink 将一些 maps 等文件进行重定向

Hook 系统调用 readlink,当检测逻辑试图访问/proc/self/maps 或/proc/self/exe 等关键文件时,返回伪造的数据。

例如,将 /proc/self/maps 中的 libfrida-agent.so 替换为其他名称。

4.通过 hook so 加载和 pthread 创建,定位 frida 检测线程,然后将线程给 hook 、

Hook 动态库加载函数(如 dlopen),阻止检测逻辑加载特定的动态库。

Hook 线程创建函数(如 pthread_create),定位检测 Frida 的线程,并停止这些线程。

5.通过逆向分析,定位 frida 检测函数,然后 hook Frida

评论