基于网络的检测
检测 Frida 的监听端口
Frida 在运行时通常会开启特定的监听端口(默认 27042
和 27043
)。可以通过扫描本地端口来检测是否存在这些端口。
1 | private boolean isFridaPortOpen() { |
检测网络流量中的 Frida 特征
- 监控应用的网络流量,检测是否存在与 Frida 工具相关的流量特征。
- 例如,检测与
localhost:27042
的连接
检测 D-Bus 协议通信
frida 使用 D-Bus 协议通信,这个通信协议并不常见
- Frida Server 的通信:
- 如果 Frida Server 被配置为通过 D-Bus 进行通信,可以检测是否有 Frida Server 在特定的 D-Bus 总线上注册。
- D-Bus 信号监听或方法调用 Hook:
- 检测是否有进程在监听特定的系统服务信号,或者通过 D-Bus 调用敏感接口。
- 可疑 D-Bus 服务注册:
- 检测是否有可疑的 D-Bus 服务名或对象路径被注册(与 Frida 的模块或库有关的名称)。
- D-Bus 通信流量分析:
- 检测 D-Bus 通信中是否存在与 Frida 相关的关键字或行为模式。
基于进程和文件的检测
检测 Frida 相关的进程
Frida 在运行时会启动特定的进程(如 frida-server
或 gadget
),这些进程通常可以通过以下方式检测到:
- Android:
- 使用
ps
命令或/proc
文件系统扫描进程列表中是否存在frida-server
或类似名称的进程。 - 检测常见的 Frida 进程名称:
frida-server
gadget
libfrida-agent.so
frida-helper
- 使用
- iOS:
- 检查是否存在
FridaGadget
或类似的动态库进程。
- 检查是否存在
示例代码(Android):
通过遍历 /proc
文件夹下的所有进程目录(以 PID 为名称的文件夹)并读取每个进程的 cmdline
文件,可以获取当前所有进程的名称。
1 | public class FridaDetection { |
扫描 task
目录
在 Linux 和 Android 系统中,每个进程都可以通过 /proc/[pid]/task
目录查看其线程信息。Frida 在注入目标进程时,可能会创建额外的线程用于执行其任务(例如动态注入、代码 Hook 或通信处理)。通过扫描 /proc/[pid]/task
目录下的线程信息,可以检测出与 Frida 相关的特征线程,从而判断 Frida 是否已注入目标进程。
原理:
- 线程命名特征:
- Frida 在注入进程后,可能会创建一些具有特定名称的线程(如
gum-js-loop
,gum-js-worker
等)。 - 这些线程名称会暴露在
/proc/[pid]/task/[tid]/comm
文件中(comm
文件记录线程的名称)。
- Frida 在注入进程后,可能会创建一些具有特定名称的线程(如
- 线程数量异常:
- Frida 注入后,目标进程通常会多出额外的线程。这些线程的数量和行为可能与正常的线程模式不一致。
- 线程行为异常:
- 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 | private boolean isFridaFilesPresent() { |
基于动态库的检测
检测已加载的 Frida 动态库
在 Linux 和 Android 系统中,/proc/self/maps
文件是一个重要的内存映射文件,它记录了当前进程的内存布局信息,包括加载的动态链接库(.so/.dylib 文件)、匿名内存区域、堆栈区域等。
Frida 在目标进程中运行时会加载一些特定的动态库(如 libfrida-agent.so
或 libfrida-gadget.so
),这些库会出现在 /proc/self/maps
文件中。因此,通过扫描这个文件,可以有效检测 Frida 是否存在。
原理
Frida 在运行时会动态加载一些特定的库,可以通过检查进程的已加载库列表来检测这些库是否存在。
- 常见的 Frida 库名称:
libfrida-gadget.so
frida-agent.so
libfrida-core.so
检测方法(Android):
- 通过
/proc/self/maps
文件:- 读取当前进程的内存映射,查找是否有 Frida 的库被加载。
1 | private boolean isFridaLibraryLoaded() { |
使用 dlopen
或 dlsym
检测 Frida 库
使用动态链接器的 API( dlopen
或 dlsym
)在运行时检查特定的库是否被加载。
- 动态库检测:
- 当 Frida 注入目标进程时,会加载其核心动态库(如
libfrida-agent.so
或libfrida-gadget.so
)。 - 使用
dlopen
可以尝试加载这些动态库,如果它们已经被加载,则不会重复加载,并且会返回一个有效的句柄。
- 当 Frida 注入目标进程时,会加载其核心动态库(如
- 符号解析检测:
- Frida 的动态库中包含一些特定的符号(如
frida_agent_main
、gum_interceptor_attach
等)。 - 使用
dlsym
可以检查这些符号是否存在,如果存在,则说明 Frida 的库已经被注入。
- Frida 的动态库中包含一些特定的符号(如
- 动态库路径特征:
- Frida 的动态库通常加载在
/data/local/tmp
或其他可疑路径中,可以通过动态库路径进行进一步验证。
- Frida 的动态库通常加载在
示例(C 代码):
1 | <C> |
inlinehook 检测:
- 函数劫持:
- Frida 使用
gum_interceptor
等组件,通过修改函数的入口点指令,将原始函数的执行流程劫持到 Frida 的 Hook 函数中。 - 通常,函数入口点的前几条指令(例如 x86 架构下的
jmp
或call
指令)会被替换为跳转指令,指向 Frida 的代码。
- Frida 使用
- 函数指令被修改:
- 被 Hook 的函数,其入口指令(如
mov
,jmp
,call
)会被替换为跳转到 Frida 插入的代码地址。 - 例如:
- 在 x86 架构中,可能是
jmp 0xdeadbeef
。 - 在 ARM 架构中,可能是
b 0x12345678
。
- 在 x86 架构中,可能是
- 被 Hook 的函数,其入口指令(如
- 内存保护特性:
- 通常情况下,代码段(如
.text
段)是只读的,如果发现代码段被修改,说明可能存在 Inline Hook。
- 通常情况下,代码段(如
检查函数入口点的指令是否被修改
通过读取目标函数的入口点指令,与正常的函数指令进行对比,判断是否存在被篡改的情况。
我们可以通过检测每个函数的开头是否有 0xd61f020058000050
这样的一段代码来判断进程是否被 frida 附加了。
自定义 Syscall 防 Hook
Frida 的 Hook 主要发生在用户态函数(如 libc 函数)上,而系统调用(Syscall)是直接由内核处理的,无法直接通过 Frida 在内核级别 Hook。因此,通过直接调用系统调用(而不是通过 libc 等用户态封装的函数)可以绕过 Frida 的 Hook。
自定义系统调用可以直接与内核交互,绕过用户态的 Hook。通过直接调用系统调用,可以检查某些关键函数是否被 Hook,并实现防御。
原理:
- 直接调用 Syscall:
- 使用
syscall
指令(Linux x86 和 x86_64 平台)或类似的机制(ARM 上的svc
指令)直接调用内核的系统调用接口,而不经过 libc 函数。 - 这样可以绕过用户态的 Hook(如 Frida 对
open
、read
等函数的劫持)。
- 使用
- 对比检测:
- 同时调用 libc 函数和 Syscall,并对比返回结果。如果结果不一致,说明用户态函数可能已被 Hook。
- 例如:调用
open
时,直接使用syscall
调用内核的open
,并与 libc 的open
返回值对比。
- 完整性校验:
- 通过直接调用系统调用,检查重要内存区域(如
.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)。
- 检测调试工具的存在,如
gdb
或lldb
。
Frida 的持久
双进程守护
Frida 双进程守护 是一种对抗检测或增强 Frida 稳定性的方法,通过运行两个相互守护的进程,在一个进程被杀死(如由于检测或防护措施)时,另一个进程会迅速重启被杀的进程,从而保证 Frida 的持续运行。
简单来说,双进程守护是一种 进程自恢复机制,它通过两个进程相互监控,确保目标进程(如 frida-server
或其他注入进程)在受到干扰或被终止时能够重新拉起。
双进程守护的工作原理
双进程守护的核心思想是:
- 启动两个进程(例如,
Process A
和Process B
),每个进程都负责监控对方的运行状态。 - 如果任意一个进程被终止,另一个进程会立即重新启动被终止的进程。
- 守护进程之间通常通过以下方式监控对方:
- 进程存在检测:例如,通过定期检查对方的 PID 是否仍然存在。
- 管道通信:例如,两个进程之间建立一个通信管道(如
pipe
或socket
),如果管道的连接断开,说明对方进程异常退出。
- 重新拉起被杀死的进程,确保两者始终在运行。
对抗双进程守护的方法
虽然双进程守护可以有效增强 Frida 的运行稳定性,但它也并非无法对抗。以下是常见的对抗方法:
批量杀死相关进程:
使用脚本同时杀死两个守护进程。例如,直接通过 pkill 命令杀死所有与 Frida 相关的进程:
1
pkill -f frida
进程组杀死:
- 确保杀死整个进程组,而非单一进程。
检测并删除守护逻辑:
- 分析守护进程的逻辑,找到其启动点(如脚本文件或守护代码),并删除或禁用它。
资源隔离:
- 使用沙箱环境隔离进程,阻止 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