Frida官方下场做Frida隐藏功能,strongfrida快要死了?17.9.0引入的新功能全解读

admin 2026-04-02 05:20:49 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细解读了Frida17.9.0版本的新功能,特别是其引入的eBPFspawngater机制。该版本旨在通过内核级技术增强隐蔽性,核心更新包括:1.使用eBPF在内核层面拦截execve系统调用来暂停进程,替代传统ptrace方案以隐藏TracerPid特征;2.实现了对被SIGSTOP信号暂停进程的注入;3.新增control-endpoint选项,允许通过localabstract等方式自定义连接端点以绕过端口检测。此外,文章还分析了新版本如何通过隐藏ptrace特征、Frida监听端口以及子进程创建监控的特征来对抗检测,并探讨了其代码实现细节。 综合评分: 85 文章分类: 逆向分析,二进制安全,恶意软件,渗透测试,红队


cover_image

Frida官方下场做Frida隐藏功能,strongfrida快要死了?17.9.0引入的新功能全解读

原创

非虫 非虫

软件安全与逆向分析

2026年3月29日 10:23 湖北

Frida17.9.0引入的新功能解读

Frida这个版本的更新看上去是将eBPF的能力继续辐射到更多的Frida功能组件上。而且重点是官方将Frida隐藏作为重要的功能提上了日程。

这个版本的更新里面,Frida隐藏的部分有:

  • 将ptrace特征彻底隐藏
  • 隐藏Frida监听端口
  • 隐藏子进程创建监控的特征

关于子进程创建监控的特征,我们这里再多说几句。国内X加密的企业版本在检测Frida时,使用了一个比较取巧的方法。重点在于监控进程的fork操作的:https://github.com/frida/frida-core/blob/main/lib/payload/fork-monitor.vala 。它会对forkvfork进行监控。里面有一行代码:

interceptor.replace (vfork_impl, fork_impl);

意味着它会对这两个接口进行InlineHook,但它们的处理器使用同一个,这两者在进程空间的数据同步上有着一些差异,比如在父进程中的一个全局变量,子进程中它们的数据访问同步规则不同,这就可以用来检测forkvfork是否被Hook,进而检测Frida。目前Frida官方并没有实现它的过检测,希望官方能够对其进行处理。这其实需要对interceptor.replace这类接口做eBPF的大手术,可能会有难度与兼容性的问题出现。

背景

Frida 17.9.0(2026年3月26日)紧跟17.8.0发布,带来了几项对安全研究者非常实用的新能力。三个核心更新值得重点关注:

  1. Linux eBPF spawn gater

    ——在内核层面拦截execve,实现比传统ptrace方案更隐蔽、更高效的进程捕获。

  2. Group-stopped PIDs注入

    ——解决了eBPF spawn gater将进程SIGSTOP后无法注入的工程难题。

  3. control-endpoint后端选项

    ——允许自定义frida-server连接端点,可通过localabstract:等非标准端口绕过Frida端口检测。

从演进线看,17.8.0完成了eBPF syscall-tracer主线收编,17.9.0则将eBPF的应用范围扩展到spawn gating领域,并围绕它做了完整的工程闭环——从内核拦截到用户态注入再到端口隐藏,形成了一条完整的反检测链路。

分析范围与方法

分析窗口以17.9.0新增功能为主线(frida-core commit 4e5a60a5..c3864a15),同时覆盖17.8.x开发周期中的关键架构变更。涉及以下子项目:

| 子项目 | 17.9.0核心提交 | 聚焦方向 | | — | — | — | | frida-core | 7 | spawn gater、group-stopped注入、control-endpoint | | frida-gum | – | 无17.9.0专属提交 | | frida-python | 3 | override_option绑定、spawn gating示例 | | frida-tools | – | releng同步 |

本文聚焦以下代码:

  1. eBPF内核侧拦截:src/linux/helpers/spawn-gater.bpf.c
  2. 用户态spawn管理:src/linux/spawn-gater.vala
  3. 宿主会话集成:src/linux/linux-host-session.vala
  4. Group-stopped注入逻辑:src/linux/frida-helper-backend.valaInjectSession
  5. 设备选项覆盖:src/frida.valaoverride_option()
  6. Droidy/Fruity后端:src/droidy/droidy-host-session.valasrc/fruity/fruity-host-session.vala

核心功能一:eBPF spawn gater

1 设计动机

Frida在macOS上早已通过DTrace实现spawn gating——当目标系统上有新进程exec时,先暂停该进程,通知Frida用户决定是否注入,再恢复执行。但Linux上一直缺少内核级的spawn gating实现,此前只在Android上通过Zygote子进程gating部分覆盖了这一需求。

17.9.0引入的eBPF spawn gater填补了这一空白。它的核心思路极为精炼:execve系统调用入口处用eBPF程序发送SIGSTOP信号,把新进程冻住,同时通过ringbuf通知用户态

这个方案的工程价值在于:eBPF程序运行在内核态,不需要ptrace附加目标进程,也不会在目标进程的/proc/pid/status中留下TracerPid痕迹,具有天然的反检测优势。

2 eBPF内核程序:spawn-gater.bpf.c

完整的eBPF程序只有55行,非常精炼。核心代码如下:

#include"frida-linux-syscalls.h"
#include<linux/bpf.h>
#include<bpf/bpf_helpers.h>
#include<bpf/bpf_tracing.h>

#define&nbsp;SIGSTOP 19
#define&nbsp;MAX_FILENAME 256

typedefstruct&nbsp;_ExecveEventExecveEvent;

struct&nbsp;_ExecveEvent
{
int&nbsp;pid;
char&nbsp;command[MAX_FILENAME];
};

struct
{
&nbsp; __uint (type, BPF_MAP_TYPE_RINGBUF);
&nbsp; __uint (max_entries,&nbsp;1&nbsp;<<&nbsp;22);
}
events&nbsp;SEC(".maps");

structtrace_event_raw_sys_enter
{
&nbsp; __u64 unused;
long&nbsp;id;
unsignedlong&nbsp;args[6];
};

SEC ("tracepoint/raw_syscalls/sys_enter")
int
on_execve_enter(struct&nbsp;trace_event_raw_sys_enter * ctx)
{
&nbsp; __s32 nr = (__s32) ctx->id;
if&nbsp;(nr != FRIDA_LINUX_SYSCALL_EXECVE)
return0;

&nbsp; ExecveEvent * e = bpf_ringbuf_reserve (&events,&nbsp;sizeof&nbsp;(ExecveEvent),&nbsp;0);
if&nbsp;(e ==&nbsp;NULL)
return0;

&nbsp; e->pid = bpf_get_current_pid_tgid () >>&nbsp;32;
constchar&nbsp;* filename = (constchar&nbsp;*) ctx->args[0];
&nbsp; bpf_probe_read_user_str (e->command,&nbsp;sizeof&nbsp;(e->command), filename);

&nbsp; bpf_ringbuf_submit (e,&nbsp;0);

&nbsp; bpf_send_signal (SIGSTOP);

return0;
}

char&nbsp;LICENSE[] SEC ("license") =&nbsp;"Dual BSD/GPL";

逐层分析关键设计决策:

挂载点选择:tracepoint/raw_syscalls/sys_enter

与17.8.0的syscall-tracer一脉相承,使用raw_syscalls/sys_enter而非tracepoint/syscalls/sys_enter_execve。原因是Android GKI 2.0内核默认关闭了FTRACE_SYSCALLS,而raw_syscalls是always-on的。通过在eBPF内部手动判断ctx->id == FRIDA_LINUX_SYSCALL_EXECVE来过滤,虽然多了一次比较,但换来了对所有Linux/Android内核的兼容性。

FRIDA_LINUX_SYSCALL_EXECVE定义在frida-linux-syscalls.h中,会根据目标架构自动选择正确的系统调用号(x86_64上为59,arm64上为221等),预编译产物覆盖了arm、arm64、x86、x86_64、mips、mips64共10个架构变体。

事件结构:ExecveEvent

struct&nbsp;_ExecveEvent&nbsp;{
int&nbsp;pid; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 进程TGID
char&nbsp;command[MAX_FILENAME];&nbsp;// execve第一个参数:可执行文件路径
};

结构极简,只包含PID和命令路径。使用bpf_get_current_pid_tgid() >> 32获取TGID(即进程PID),通过bpf_probe_read_user_str从用户空间安全读取execve的第一个参数(文件名)。

核心机制:bpf_send_signal(SIGSTOP)

这是整个设计最精妙的一行。bpf_send_signal()是Linux 5.3引入的BPF helper,允许eBPF程序直接向当前任务发送信号。调用bpf_send_signal(SIGSTOP)会让正在执行execve的进程立即被内核暂停——进程进入group-stop状态。

这与ptrace的PTRACE_ATTACH完全不同:

  • ptrace会在目标进程的/proc/pid/status中设置TracerPid字段,容易被反调试检测

  • SIGSTOP

    导致的group-stop是正常的进程状态,不涉及调试器附加

  • eBPF程序运行在内核上下文,对目标进程完全透明

ringbuf通信

使用BPF_MAP_TYPE_RINGBUF(大小4MB,即1 << 22)。先bpf_ringbuf_reserve预留空间、填充数据、再bpf_ringbuf_submit提交。比BPF_MAP_TYPE_PERF_EVENT_ARRAY更高效,不需要per-CPU buffer。

3 用户态管理:SpawnGater类

src/linux/spawn-gater.vala实现了完整的用户态管理逻辑,总计154行。

SpawnGater
├── start() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 加载eBPF程序,attach tracepoint,监听ringbuf
├── stop() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 卸载eBPF,恢复所有pending进程
├── enumerate_pending_spawn() &nbsp;// 返回当前被拦截的进程列表
├── try_resume() &nbsp; &nbsp; // 恢复指定进程
├── signal spawn_added &nbsp; &nbsp;// 通知上层有新进程被拦截
└── signal spawn_removed &nbsp;// 通知上层进程已恢复

启动流程

publicvoid&nbsp;start () throws Error {
var&nbsp;obj = BpfObject.open ("spawn-gater.elf",
&nbsp; &nbsp; &nbsp; &nbsp; Frida.Data.HelperBackend.get_spawn_gater_elf_blob ().data);
var&nbsp;events = obj.maps.get_by_name ("events");
&nbsp; &nbsp; obj.prepare ();
&nbsp; &nbsp; events_reader =&nbsp;new&nbsp;BpfRingbufReader (events);
&nbsp; &nbsp; obj.load ();

foreach&nbsp;(var&nbsp;program in obj.programs) {
var&nbsp;link = program.attach ();
&nbsp; &nbsp; &nbsp; &nbsp; links.add (link);
&nbsp; &nbsp; }

// 设置epoll监听ringbuf的fd
&nbsp; &nbsp; events_channel =&nbsp;new&nbsp;IOChannel.unix_new (events.fd);
var&nbsp;src =&nbsp;new&nbsp;IOSource (events_channel, IOCondition.IN);
var&nbsp;state =&nbsp;new&nbsp;EventsWatchState (this);
&nbsp; &nbsp; src.set_callback (state.on_ready);
&nbsp; &nbsp; src.attach (MainContext.get_thread_default ());
&nbsp; &nbsp; events_source = src;
}

这里复用了17.8.0引入的BpfObjectBpfRingbufReader基础设施。预编译的spawn-gater.elf以资源blob形式内嵌在frida-helper中,运行时通过libbpf加载。

BpfRingbufReader内部使用epoll监听ringbuf的文件描述符。当内核侧有新事件写入时,通过GLib的IOSource回调on_ready,进而调用process_pending_events()拉取事件。

事件处理

privatevoid&nbsp;handle_event (ExecveEvent * e) {
var&nbsp;info = HostSpawnInfo (e->pid, (string) e->command);
&nbsp; &nbsp; pending_spawn[e->pid] = info;
&nbsp; &nbsp; spawn_added (info);
}

每个事件被解析为HostSpawnInfo(包含pid和可执行文件路径),存入pending_spawn字典,并通过spawn_added信号通知上层。

恢复机制

publicbool&nbsp;try_resume (uint&nbsp;pid) {
&nbsp; &nbsp; HostSpawnInfo? spawn;
if&nbsp;(!pending_spawn.unset (pid, out spawn))
returnfalse;
&nbsp; &nbsp; spawn_removed (spawn);
&nbsp; &nbsp; perform_resume.begin (pid);
returntrue;
}

privateasyncvoid&nbsp;perform_resume (uint&nbsp;pid) {
try&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; yield helper.resume (pid,&nbsp;null);
&nbsp; &nbsp; }&nbsp;catch&nbsp;(GLib.Error e) {
if&nbsp;(e is Error.INVALID_ARGUMENT)
Posix.kill ((Posix.pid_t) pid,&nbsp;Posix.Signal.CONT);
&nbsp; &nbsp; }
}

恢复时优先尝试通过helper.resume()(ptrace方式),如果失败则退回kill(pid, SIGCONT)直接发送继续信号。这个双路径设计保证了健壮性:如果进程已经被注入过(由InjectSession管理),走ptrace路径;否则走简单的SIGCONT。

4 宿主会话集成

linux-host-session.vala中的修改将spawn gater无缝集成到Frida的现有架构中:

publicoverrideasyncvoid&nbsp;enable_spawn_gating (...) {
// 先预加载helper(确保64位和32位helper都已就绪)
var&nbsp;helper_process = helper as LinuxHelperProcess;
if&nbsp;(helper_process !=&nbsp;null)
&nbsp; &nbsp; &nbsp; &nbsp; yield helper_process.preload (cancellable);

var&nbsp;gater = ensure_spawn_gater ();
if&nbsp;(gater.state == STOPPED) {
// Android上允许eBPF启动失败(可能缺少CAP_BPF)
// 纯Linux上直接抛异常
&nbsp; &nbsp; &nbsp; &nbsp; #if&nbsp;ANDROID
try&nbsp;{ gater.start (); }&nbsp;catch&nbsp;(Error e) { }
&nbsp; &nbsp; &nbsp; &nbsp; #else
&nbsp; &nbsp; &nbsp; &nbsp; gater.start ();
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;#endif
&nbsp; &nbsp; }

&nbsp; &nbsp; #if&nbsp;ANDROID
&nbsp; &nbsp; yield robo_launcher.enable_spawn_gating (cancellable);
&nbsp; &nbsp;&nbsp;#endif
}

关键设计点:

  1. 预加载helper

    preload()方法确保frida-helper-64和frida-helper-32都已启动,避免后续注入时的延迟。

  2. Android双轨制

    :在Android上,eBPF spawn gater与传统的Zygote-based RoboLauncher并行工作。eBPF覆盖所有通过execve启动的原生进程,RoboLauncher覆盖Java层fork出的App进程。两者产出的spawn事件统一汇聚到同一个spawn_added信号。

  3. 容错处理

    :Android上eBPF启动失败不影响RoboLauncher工作,旧设备仍然可以通过Java层方案完成spawn gating。

enumerate_pending_spawn()也做了合并:

publicoverrideasync&nbsp;HostSpawnInfo[] enumerate_pending_spawn (...) {
var&nbsp;result =&nbsp;new&nbsp;HostSpawnInfo[0];
if&nbsp;(spawn_gater !=&nbsp;null)
foreach&nbsp;(var&nbsp;spawn in spawn_gater.enumerate_pending_spawn ())
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result += spawn;
&nbsp; &nbsp; #if&nbsp;ANDROID
foreach&nbsp;(var&nbsp;spawn in robo_launcher.enumerate_pending_spawn ())
&nbsp; &nbsp; &nbsp; &nbsp; result += spawn;
&nbsp; &nbsp;&nbsp;#endif
return&nbsp;result;
}

恢复时也是双路径尝试:

protectedoverrideasyncvoid&nbsp;perform_resume (uint&nbsp;pid, ...) {
if&nbsp;(spawn_gater !=&nbsp;null&nbsp;&& spawn_gater.try_resume (pid))
return;
&nbsp; &nbsp; #if&nbsp;ANDROID
if&nbsp;(robo_launcher.try_resume (pid))
return;
&nbsp; &nbsp;&nbsp;#endif
&nbsp; &nbsp; yield helper.resume (pid, cancellable);
}

核心功能二:Group-stopped PIDs注入

1 问题根源

eBPF spawn gater通过bpf_send_signal(SIGSTOP)将进程冻住。但这产生了一个新的工程问题:被SIGSTOP暂停的进程处于group-stop状态(/proc/pid/stat中标记为T),Frida原有的注入路径无法正确处理这种状态的进程。

原因是Frida的注入基于ptrace。标准流程是:

ptrace(SEIZE, pid) → ptrace(INTERRUPT, pid) → waitpid() → 注入 → ptrace(DETACH, pid)

但对于已经处于group-stop的进程,ptrace(INTERRUPT, pid)的行为不符合预期——进程已经停了,再发INTERRUPT会导致信号等待逻辑卡住或返回错误。

2 解决方案

commit 0bee9cba通过以下策略解决了这个问题:

第一步:检测group-stop状态

新增query_process_state()方法,通过读取/proc/<tid>/stat来判断进程状态:

privatestaticchar&nbsp;query_process_state (uint&nbsp;tid) {
try&nbsp;{
string&nbsp;stat;
&nbsp; &nbsp; &nbsp; &nbsp; FileUtils.get_contents ("/proc/%u/stat".printf (tid), out stat);
int&nbsp;paren_end = stat.last_index_of_char (')');
if&nbsp;(paren_end !=&nbsp;-1&nbsp;&& paren_end +&nbsp;2&nbsp;< stat.length)
return&nbsp;stat[paren_end +&nbsp;2];
&nbsp; &nbsp; }&nbsp;catch&nbsp;(FileError e) {
&nbsp; &nbsp; }
return'?';
}

/proc/pid/stat的格式是pid (comm) state ...,状态字符紧跟最后一个右括号后的空格。T表示stopped(包括group-stop和ptrace-stop),S表示sleeping,R表示running。代码使用last_index_of_char(')')定位最后一个),因为进程名(comm字段)本身可能包含括号和空格,从后向前搜索才能避免误解析。

第二步:差异化的ptrace附加流程

bool&nbsp;was_stopped = seize_supported && query_process_state (tid) ==&nbsp;'T';
// ...
if&nbsp;(seize_supported) {
if&nbsp;(was_stopped) {
&nbsp; &nbsp; &nbsp; &nbsp; was_group_stopped =&nbsp;true;
&nbsp; &nbsp; &nbsp; &nbsp; yield wait_for_next_stop (cancellable);
Posix.kill ((Posix.pid_t) pid,&nbsp;Posix.Signal.CONT);
&nbsp; &nbsp; }&nbsp;else&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; ptrace (INTERRUPT, tid);
&nbsp; &nbsp; &nbsp; &nbsp; yield wait_for_signal (TRAP, cancellable);
&nbsp; &nbsp; }
}

对group-stopped进程的处理与正常进程完全不同:

  1. 跳过ptrace(INTERRUPT)——进程已经停了,不需要再中断
  2. 直接wait_for_next_stop()——等待ptrace报告当前的stop状态
  3. 发送SIGCONT——将进程从group-stop切换到ptrace-stop,使后续的注入操作能正常进行

第三步:注入后保持暂停

var&nbsp;session = yield InjectSession.open (pid, cancellable);
RemoteAgent agent = yield session.inject (spec, cancellable);
if&nbsp;(session.was_group_stopped)
&nbsp; &nbsp; backend.suspended_by_inject[pid] = session;
else
&nbsp; &nbsp; session.close ();

如果目标进程原本就是group-stopped的(由eBPF spawn gater暂停),注入完成后不立即detach,而是保持InjectSession打开,把session存入suspended_by_inject字典。这样进程仍然处于暂停状态,等待用户显式resume()

第四步:resume时清理

InjectSession inject_session;
if&nbsp;(backend.suspended_by_inject.unset (pid, out inject_session)) {
&nbsp; &nbsp; inject_session.close (); &nbsp;// ptrace detach,进程恢复执行
returnnull;
}

当用户调用resume(pid)时,从suspended_by_inject中取出session并close,触发ptrace detach,进程恢复执行。

3 完整链路

将eBPF spawn gater与group-stopped注入串联起来,完整的进程捕获-注入-恢复流程如下:

1. 目标进程调用 execve()
2. eBPF tracepoint 触发 on_execve_enter()
3. bpf_send_signal(SIGSTOP) → 进程进入 group-stop
4. bpf_ringbuf_submit() → 事件写入 ringbuf
5. SpawnGater.process_pending_events() 收到事件
6. spawn_added 信号通知用户
7. 用户决定注入 → attach(pid)
8. InjectSession.open() 检测到 'T' 状态
9. ptrace(SEIZE) → wait → SIGCONT → 注入代码
10. session 存入 suspended_by_inject(进程仍暂停)
11. 用户调用 resume(pid)
12. InjectSession.close() → ptrace(DETACH) → 进程恢复执行

整条链路的关键在于:eBPF拦截阶段完全不涉及ptrace,目标进程的TracerPid始终为0。只在注入阶段短暂使用ptrace(SEIZE)——此时TracerPid会被设置——但注入完成后立即ptrace(DETACH)将其清零。与传统的全程ptrace跟踪相比,TracerPid暴露窗口从”整个生命周期”缩短到”毫秒级注入窗口”。

核心功能三:control-endpoint后端选项

1 override_option()框架

17.9.0在Device类上新增了override_option()方法,建立了后端选项覆盖的通用框架:

publicvoid&nbsp;override_option (string&nbsp;name, Variant val) throws Error {
&nbsp; &nbsp; Value v;
switch&nbsp;(val.classify ()) {
case&nbsp;BOOLEAN:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v = Value (typeof (bool));
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v.set_boolean (val.get_boolean ());
break;
case&nbsp;STRING:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v = Value (typeof (string));
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v.set_string (val.get_string ());
break;
// ... 还支持 INT64, UINT64, DOUBLE
default:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw&nbsp;new&nbsp;Error.INVALID_ARGUMENT ("Unsupported option type");
&nbsp; &nbsp; }

&nbsp; &nbsp; lock (host_session_options) {
if&nbsp;(host_session_options ==&nbsp;null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; host_session_options =&nbsp;new&nbsp;HostSessionOptions ();
&nbsp; &nbsp; &nbsp; &nbsp; host_session_options.map[name] = v;
&nbsp; &nbsp; }
}

选项存储在HostSessionOptions中(一个Gee.HashMap<string, Value?>),创建host session时通过copy()方法线程安全地传递给后端Provider:

HostSessionOptions? opts;
lock (host_session_options)
&nbsp; &nbsp; opts = (host_session_options !=&nbsp;null) ? host_session_options.copy () :&nbsp;null;
var&nbsp;session = yield provider.create (manager, opts, cancellable);

设计上,选项在建立host session时生效。如果session已存在,更新后的值在下次连接时才应用。

2 Droidy(Android)control-endpoint

Droidy后端接受任意ADB支持的endpoint格式:

string&nbsp;control_endpoint = ("tcp:%"&nbsp;+&nbsp;uint16.FORMAT_MODIFIER +&nbsp;"u")
&nbsp; &nbsp; .printf (DEFAULT_CONTROL_PORT); &nbsp;// 默认 "tcp:27042"

if&nbsp;(options !=&nbsp;null) {
&nbsp; &nbsp; Value? control_endpoint_val = options.map["control-endpoint"];
if&nbsp;(control_endpoint_val !=&nbsp;null) {
if&nbsp;(!control_endpoint_val.holds (typeof (string)))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw&nbsp;new&nbsp;Error.INVALID_ARGUMENT (
"The control-endpoint option must be a string");
&nbsp; &nbsp; &nbsp; &nbsp; control_endpoint = control_endpoint_val.get_string ();
&nbsp; &nbsp; }
}

host_session =&nbsp;new&nbsp;DroidyHostSession (device_details,&nbsp;this, control_endpoint);

control_endpoint随后在连接frida-server时使用:

// 原来硬编码:
// var stream = yield channel_provider.open_channel ("tcp:27042", cancellable);
// 现在可配置:
var&nbsp;stream = yield channel_provider.open_channel (control_endpoint, cancellable);

open_channel()底层通过ADB的forwardreverse机制建立连接。ADB支持的endpoint格式包括:

| 格式 | 说明 | 示例 | | — | — | — | | tcp:<port> | TCP端口 | tcp:27042 | | localabstract:<name> | Linux抽象Unix socket | localabstract:/my-frida-server | | localreserved:<name> | 保留Unix socket | localreserved:frida | | localfilesystem:<path> | 文件系统Unix socket | localfilesystem:/data/frida.sock |

3 Fruity(iOS)control-endpoint

Fruity后端仅支持TCP端口,从endpoint字符串中解析端口号:

uint16&nbsp;control_port = DEFAULT_CONTROL_PORT; &nbsp;// 27042

if&nbsp;(options !=&nbsp;null) {
&nbsp; &nbsp; Value? control_endpoint_val = options.map["control-endpoint"];
if&nbsp;(control_endpoint_val !=&nbsp;null) {
unownedstring&nbsp;control_endpoint = control_endpoint_val.get_string ();
if&nbsp;(!control_endpoint.has_prefix ("tcp:"))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw&nbsp;new&nbsp;Error.INVALID_ARGUMENT (
"The control-endpoint must be TCP-flavored");
&nbsp; &nbsp; &nbsp; &nbsp; control_port = (uint16)&nbsp;uint.parse (control_endpoint[4:]);
&nbsp; &nbsp; }
}

iOS由于走的是usbmuxd或RemoteXPC隧道,底层实现与ADB不同,只能转发TCP端口。

4 Python绑定

# frida/core.py
@cancellable
defoverride_option(self, name:&nbsp;str, value:&nbsp;Any) ->&nbsp;None:
"""Override a backend-specific option"""
&nbsp; &nbsp; self._impl.override_option(name, value)

C扩展层通过PyGObject_unmarshal_variant将Python对象转换为GVariant,再调用frida_device_override_option()

对于命令行用户,fridafrida-trace等工具暂时还未直接暴露--control-endpoint参数,需要通过Python API使用。

其他重要更新

64位设备跳过32位helper(17.9.0)

commit be239982为纯64位Android设备(如Pixel 7+)跳过32位helper的启动尝试:

if&nbsp;(sizeof (void&nbsp;*) ==&nbsp;8&nbsp;&&
&nbsp; &nbsp; _query_android_system_property ("ro.product.cpu.abilist32") ==&nbsp;"")
&nbsp; &nbsp; throw&nbsp;new&nbsp;Error.NOT_SUPPORTED (
"Android system does not support 32-bit processes");

通过检查ro.product.cpu.abilist32系统属性是否为空来判断。在纯64位设备上避免无意义的32位helper spawn失败,减少启动耗时和日志噪音。

XCFramework devkit打包(17.9.0)

frida-gum新增了将devkit打包为XCFramework的工具支持(由sewerynplazuk贡献)。XCFramework是Apple推荐的多架构框架分发格式,支持在一个bundle中包含iOS真机(arm64)、iOS模拟器(arm64/x86_64)和macOS等多个平台的库。这对于在Xcode项目中集成Frida SDK的开发者来说减少了手动管理fat binary和lipo的工作。


以下更新来自17.8.x开发周期:

Android轻量级Zygote Hook(17.8.x)

commit 3e778a99重写了Android的spawn gating机制,从”注入内部agent到Zygote”改为轻量级方案:

  1. 通过/proc/$pid/mem向Zygote进程的代码区写入微型payload(zymbiote,约740-920字节,按架构不同而变化)
  2. Patch android.os.Process.setArgV0Native()的ArtMethod,跳转到payload
  3. payload作为代理,链式调用原始setArgV0Native
  4. 同时建立Unix socket连接回frida-core,报告新App的spawn

不再需要注入frida-java-bridge到Zygote,去掉了对system_server代码注入的依赖。

frida-helper.dex统一化(17.8.x)

commit 1b908e69将frida-helper.dex的使用从非root场景扩展到所有场景,统一了Android helper架构。唯一损失的功能是launch-timeout抑制。

Interceptor FORCE标志(17.8.x)

frida-gum新增GUM_ATTACH_FLAGS_FORCE标志(commit b665c979),允许对”太短”的函数强制inline hook:

typedefenum&nbsp;{
&nbsp; GUM_ATTACH_FLAGS_NONE &nbsp; &nbsp; &nbsp; &nbsp;=&nbsp;0,
&nbsp; GUM_ATTACH_FLAGS_UNIGNORABLE = (1&nbsp;<<&nbsp;0),
&nbsp; GUM_ATTACH_FLAGS_FORCE &nbsp; &nbsp; &nbsp; = (1&nbsp;<<&nbsp;1), &nbsp;// 新增
} GumAttachFlags;

正常情况下,如果目标函数体太短(不够放跳转指令+保存上下文),Interceptor会拒绝attach。启用FORCE标志后,Interceptor会直接覆写函数末尾后面的字节。这在函数之间有对齐padding的情况下是安全的,frida-core的ELF RTLD notifier强制hook(commit 35c5d862)即使用了此标志。

RISC-V初步支持(17.8.x)

commit 747a9cd1在frida-core中添加了RISC-V架构的初步支持,为后续在RISC-V设备上运行Frida铺平道路。

Python绑定改进

  • 新增spawn gating完整示例(examples/spawn_gating.py),展示了enable_spawn_gating()→监听spawn-added→按条件注入→resume()的标准用法
  • 修复child gating示例中过时的API调用(getExportByNamegetGlobalExportByNameMemory.readUtf8String(args[0])args[0].readUtf8String()
  • 类型标注改进:@cancellable装饰器改进、flag字面量类型支持

frida-gum其他修复

| 提交 | 说明 | | — | — | | b75ee4a2 | 修复arm64 ucontext记录解析 | | bd80b1a6 | 修复ELF32的GNU hash解析 | | e9a92eb4 | 改进ELF文件偏移验证 | | ed3388d7 | 验证HASH和GNU_HASH节的边界 | | 414c40e9 | Android APK libs在enumerateRanges()中的处理 | | 2f898792 | 处理__pthread_start符号后缀 |

提交归纳

frida-core 17.9.0核心提交

| 提交 | 分类 | 说明 | | — | — | — | | 465698a9 | 新功能 | eBPF spawn gater(NSEcho & oleavr) | | 0bee9cba | 新功能 | Group-stopped PIDs注入支持 | | be239982 | 优化 | 64位设备跳过32位helper | | f9629316 | 基础设施 | HostSessionOptions.copy() | | 3df05876 | 新功能 | Device.override_option()方法 | | d2f2c400 | 新功能 | Droidy control-endpoint选项 | | 1f77154b | 新功能 | Fruity control-endpoint选项 |

frida-core 17.8.x周期关键提交

| 提交 | 分类 | 说明 | | — | — | — | | 3e778a99 | 重构 | Android轻量级Zygote hooking(zymbiote) | | 1b908e69 | 重构 | frida-helper.dex统一化 | | 747a9cd1 | 新功能 | RISC-V初步支持 | | 55c8bcb8 | 修复 | Fruity USB启动/关闭竞态 |

frida-gum(17.8.x周期)

| 提交 | 分类 | 说明 | | — | — | — | | b665c979 | 新功能 | Interceptor FORCE attach标志 | | 35c5d862 | 增强 | ELF RTLD notifier强制hook | | 2f898792 | 修复 | Android __pthread_start符号后缀处理 |

frida-python(17.9.0)

| 提交 | 分类 | 说明 | | — | — | — | | cc9a78e | 新功能 | Device.override_option()绑定 | | 22ed1fa | 文档 | spawn gating示例 | | 9b15bde | 修复 | child gating示例更新 |

总结

Frida 17.9.0的更新虽然只有7个核心提交,但形成了清晰的技术闭环:

  1. eBPF spawn gater

    在内核层面拦截进程创建,不留ptrace痕迹

  2. Group-stopped注入

    解决了eBPF拦截后的代码注入工程问题

  3. control-endpoint

    允许隐藏frida-server的网络指纹

三者串联使用,构成了一条完整的Frida反检测链路:eBPF无痕拦截 → 短窗口ptrace注入 → 非标准端口/socket通信。对于安全研究者来说,这意味着在对抗重防护App时多了一套可靠的工程选择。

从Frida的技术演进看,17.8.0和17.9.0连续两个版本都在深化eBPF的应用——从系统调用跟踪到进程捕获,eBPF正在成为Frida在Linux/Android平台上的核心基础设施之一。

同时也希望Frida作者在本体隐藏这一点上再接再厉!


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:软件安全与逆向分析 非虫 非虫《Frida官方下场做Frida隐藏功能,strongfrida快要死了?17.9.0引入的新功能全解读》

评论:0   参与:  0