Frida17.12.0的功能与变化"

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

cover_image

Frida17.12.0的功能与变化

原创

非虫
非虫

软件安全与逆向分析

2026年6月12日 16:14
湖北

在小说阅读器读本章

去阅读

Frida17.12.0发布于2026年6月10日。官方发布说明列出的更新集中在四个方向:Gum/GumJS新增代码形状分析接口,Linux注入器调整触发点选择和agent装载路径,Interceptor增加细粒度刷新接口,Windows arm64相关的Stalker和Exceptor修复。

更新范围

17.12.0对应的主要提交如下。

子仓库 提交 更新内容
frida-gum 67a1b9c 新增gum_process_find_function_range()
frida-gum 5e2fd31 新增GumControlFlowGraph模块
frida-gum b3273ea 在GumJS暴露Process.findFunctionRange()和ControlFlowGraph
frida-gum f04c3e3 调整控制流图公开命名,例如findBlockContaining()和capacity
frida-gum 779f9b0 新增gum_interceptor_flush_function()和gum_interceptor_flush_listener()
frida-core a0a8838 eBPF采样不可用时回退到/proc/<pid>/task/<tid>/syscall
frida-core 33e5879 按实际bootstrap stub大小筛选触发函数
frida-core 880f7ac 采样线程栈,寻找可容纳注入stub的libc调用点
frida-core f0cc3b4 安装和恢复触发点时使用对称写回流程
frida-core f51327f agent通过/proc/<pid>/fd/<fd>路径装载
frida-core 21dc508 捕获libbpf诊断日志并写入错误信息

这些提交覆盖了两个层面。frida-gum侧增加公开API和GumJS绑定,主要供脚本和嵌入式使用;frida-core侧调整Linux注入实现,主要影响frida-server、attach和注入稳定性。

函数范围定位

frida-gum提交67a1b9c新增gum_process_find_function_range(),用于根据函数内部地址查找该地址所在函数的连续代码范围。

头文件中的声明如下:

GUM_API gboolean gum_process_find_function_range(gconstpointer address,
    GumMemoryRange * range);

该接口不要求目标二进制保留符号。实现侧会优先读取平台的unwind信息:POSIX平台通过_Unwind_Find_FDE并解析FDE编码,Windows平台通过RtlLookupFunctionEntry查询运行时函数表。若unwind信息不可用,则退回到符号边界查询。

GumJS侧对应接口是Process.findFunctionRange()。它接收一个NativePointer,返回{ base, size }形式的内存范围;如果无法定位函数范围,则返回null。

官方测试中的调用方式如下:

const f = ptr.strip();
const range = Process.findFunctionRange(f);
send(range !== null);
send(range.base.compare(f) <= 0 &&
    f.compare(range.base.add(range.size)) < 0);

在实际脚本中,ptr可以替换为某个导出函数地址、符号地址,或者任意落在目标函数内部的指令地址。例如:

const openPtr = Module.getGlobalExportByName('open');
const range = Process.findFunctionRange(openPtr);

if (range !== null) {
console.log('base=' + range.base + ', size=' + range.size);
}

该接口可用于确定函数边界、检查某个Hook点是否位于同一函数内,或者在指令重定位前获取可分析范围。

控制流图接口

frida-gum提交5e2fd31新增GumControlFlowGraph。该模块基于Capstone反汇编函数代码,构建基本块、前驱、后继和支配关系。提交说明中提到,它使用resolver定位函数范围,并通过直接分支发现多段函数,例如热路径和冷路径分离的函数片段。

公开头文件中的主要接口如下:

typedefgboolean(* GumControlFlowGraphFindRangeFunc)(gconstpointer address,
    GumMemoryRange * range, gpointer user_data);

typedefgboolean(* GumFoundDominatingSiteFunc)(gconstpointer site,
    gsize capacity, gpointer user_data);

GUM_API GumControlFlowGraph * gum_control_flow_graph_new(gconstpointer entry,
    cs_arch arch, cs_mode mode, GumControlFlowGraphFindRangeFunc find_range,
    gpointer user_data);
GUM_API GumControlFlowGraph * gum_control_flow_graph_new_for_function(
    gconstpointer entry_point);
GUM_API gboolean gum_control_flow_graph_dominates(GumControlFlowGraph * self,
    gconstpointer a, gconstpointer b);
GUM_API voidgum_control_flow_graph_enumerate_dominating_sites(
    GumControlFlowGraph * self, gconstpointer target,
    GumFoundDominatingSiteFunc func, gpointer user_data);

其中gum_control_flow_graph_dominates()用于判断地址a是否支配地址b。gum_control_flow_graph_enumerate_dominating_sites()会枚举支配目标地址的可写入位置,并给出capacity,表示从该位置开始、不会被其它控制流边落入的连续字节数。

测试用例中构造了一个类似start_thread的短函数,用来验证支配关系和可写入位置。

TESTCASE (single_range_dominators_and_sites)
{
staticconst guint8 code[] = {
0x48, 0x85, 0xc0, 0x74, 0x04, 0xff, 0xd0, 0xeb, 0x03, 0xff, 0xd0, 0x90,
0xc3,
  };
  GumTestCode layout = { code, sizeof (code), NULL, 0 };
  GumControlFlowGraph * cfg;
  GumCollectedSites sites = { { NULL, }, };

  cs_arch_register_x86 ();

  cfg = gum_control_flow_graph_new (code, CS_ARCH_X86, CS_MODE_64,
      gum_test_find_range, &layout);

  g_assert_true (
      gum_control_flow_graph_dominates (cfg, code + 0x00, code + 0x09));
  g_assert_true (
      gum_control_flow_graph_dominates (cfg, code + 0x03, code + 0x09));
  g_assert_false (
      gum_control_flow_graph_dominates (cfg, code + 0x05, code + 0x09));

  gum_control_flow_graph_enumerate_dominating_sites (cfg, code + 0x09,
      gum_test_collect_site, &sites);
}

这段测试说明了接口的判定语义:只有位于所有到达目标路径上的指令才算支配目标;被条件分支绕过的指令不会被当作支配点。

GumJS控制流图用法

frida-gum提交b3273ea把控制流图接口暴露到GumJS。17.12.0最终公开的JS对象包括ControlFlowGraph和BasicBlock。

官方测试中的调用方式如下:

const cfg = newControlFlowGraph(ptr);
const entry = cfg.entryBlock;

send(entry !== null);
send(cfg.blocks.length > 0);
send(cfg.findBlockContaining(entry.start).start.equals(entry.start));
send(cfg.dominates(entry.start, entry.start));

const insns = entry.instructions;
send(insns.length > 0);
send(insns[0].address.equals(entry.start));
send(Array.isArray(entry.successors));
send(Array.isArray(entry.predecessors));

const sites = cfg.enumerateDominatingSites(entry.start);
send(sites.length > 0);
send(sites[0].address.equals(entry.start));
send(typeof sites[0].capacity === 'number');

实际使用时,可以先取一个函数地址,再构建控制流图:

const target = Module.getGlobalExportByName('pthread_create');
const cfg = newControlFlowGraph(target);

console.log('block count=' + cfg.blocks.length);

for (const site of cfg.enumerateDominatingSites(target)) {
console.log('site=' + site.address + ', capacity=' + site.capacity);
}

如果脚本需要检查某个地址属于哪个基本块,可以使用findBlockContaining():

const block = cfg.findBlockContaining(target);

if (block !== null) {
console.log('block start=' + block.start);
console.log('block end=' + block.end);
console.log('instruction count=' + block.instructions.length);
}

这组接口可用于Hook点筛选、函数内控制流查看、基本块粒度分析等场景。

C层控制流图用法

嵌入Gum或开发原生组件时,可直接使用C接口。

static gboolean
on_site(gconstpointer site, gsize capacity, gpointer user_data)
{
  g_print (\"site=%p capacity=%zu\
\", site, capacity);
return TRUE;
}

void
inspect_target(gconstpointer target)
{
  GumControlFlowGraph * cfg;

  cfg = gum_control_flow_graph_new_for_function (target);
  gum_control_flow_graph_enumerate_dominating_sites (cfg, target, on_site, NULL);
  gum_control_flow_graph_free (cfg);
}

如果只查询函数范围,可直接使用gum_process_find_function_range():

GumMemoryRange range;

if (gum_process_find_function_range (target, &range))
{
  g_print (\"function range: %p - %p\
\",
      (void *) range.base_address,
      (void *) (range.base_address + range.size));
}

Linux注入器触发点采样

17.12.0中,frida-core对Linux的proc-mem注入链路做了多处修改。相关代码位于src/linux/proc-mem-injector.vala。

提交a0a8838增加了采样回退逻辑。优先使用ActivitySampler采样;如果eBPF或perf因为权限限制不可用,则读取/proc/<pid>/task/<tid>/syscall。

privateasyncuint64 discover_trigger (ProcMapsSnapshot maps, RegionLayout region, StackRendezvous rendezvous,
uint64 mmap_impl, uint64 exclude, Cancellable? cancellable) throws Error, IOError {
Gee.List<SampledStack> stacks;

var sampler = new ActivitySampler (pid);
try {
    sampler.start ();
    yield sleep_ms (SAMPLE_WINDOW_MS);
    sampler.stop ();
    stacks = sampler.stacks;
  } catch (Error e) {
    stacks = yield sample_threads_via_proc (maps, cancellable);
  }

return hottest_libc_function (maps, region, rendezvous, mmap_impl, stacks, exclude);
}

提交880f7ac扩展/proc路径的采样内容。修改前只读取线程当前PC;修改后还会读取栈指针附近的若干栈字,形成候选调用帧。

privateuint64[] read_thread_frames (ProcMapsSnapshot maps, uint tid) throws Error {
string contents;
try {
    FileUtils.get_contents (\"/proc/%u/task/%u/syscall\".printf (pid, tid), out contents);
  } catch (FileError e) {
return {};
  }

string[] fields = contents.strip ().split (\" \");
if (fields.length < 2)
return {};

uint64 pc;
if (!uint64.try_parse (fields[fields.length - 1], out pc, null, 0) || pc == 0)
return {};

uint64 sp;
var stack = (uint64.try_parse (fields[fields.length - 2], out sp, null, 0) && sp != 0)
    ? maps.find_mapping (sp)
    : null;
if (stack == null)
returnnewuint64[] { pc };

uint depth = uint.min (STACK_SCAN_DEPTH, (uint) ((stack.end - sp) / 8));
var frames = newuint64[1 + depth];
  frames[0] = pc;
for (uint i = 0; i != depth; i++)
    frames[1 + i] = mem.read_pointer (sp + (uint64) i * 8);
return frames;
}

这段逻辑用于处理多线程目标中的一种情况:线程可能停在很短的syscall wrapper里,当前PC所在函数空间不足以写入bootstrap stub。读取栈上的返回地址后,注入器可在调用链中查找可容纳stub的libc函数作为触发点。

提交33e5879调整了触发函数大小判定方式。修改前使用固定最小长度;修改后先构造实际要写入的stub,再用实际长度筛选候选函数。

privateuint64 trigger_stub_footprint (uint64 sample_target, RegionLayout region, StackRendezvous rendezvous,
uint64 mmap_impl) throws Error {
uint8[] stub = build_trigger_stub (sample_target, rendezvous.cas, mmap_impl, 0, region.total,
    region.entry_offset);
return BOOTSTRAP_OFFSET + stub.length;
}

触发点写入与恢复

提交f0cc3b4调整了触发函数恢复过程。恢复时先让调用者停在触发点入口,再写回stub覆盖区域,最后恢复函数前导指令。

privateasyncvoid restore_trigger (uint64 target, uint8[] original) throws Error, IOError {
  block_callers (target);
  yield sleep_ms (DRAIN_MS);
  mem.write_memory (target + BOOTSTRAP_OFFSET, original[BOOTSTRAP_OFFSET:original.length]);
  restore_prologue (target, original);
}

对应的安装流程也先调用block_callers(),等待短时间后再写入bootstrap。

privateasyncvoid install_bootstrap (uint64 target, uint8[] stub) throws Error, IOError {
  block_callers (target);
  yield sleep_ms (DRAIN_MS);
  mem.write_memory (target + BOOTSTRAP_OFFSET, stub);
  release_callers (target);
}

该修改针对的是多线程进程中触发函数正在被其它线程执行的情况,避免恢复过程中出现半写入的函数前导区域。

Agent装载路径

提交f51327f修改了Linux loader中agent的dlopen()路径。旧路径使用/proc/self/fd/<fd>,新路径改为/proc/<pid>/fd/<fd>。

补丁中的核心代码如下:

staticpid_t
frida_getpid(void)
{
return frida_syscall_0 (SYS_getpid);
}
libc->sprintf (agent_path, \"/proc/%d/fd/%d\", frida_getpid (), agent_codefd);

ctx->agent_handle = libc->dlopen (agent_path, libc->dlopen_flags, pretend_caller_addr);
if (ctx->agent_handle == NULL)
goto dlopen_failed;

提交说明指出,附加调试器会在动态链接器的库加载事件中读取dlopen()传入的路径。若路径是/proc/self/fd/<fd>,调试器侧解析到的是调试器自己的fd表,而不是目标进程的fd表,可能导致读取失败并让加载线程停在动态链接器断点处。改成/proc/<pid>/fd/<fd>后,目标进程内部和外部观察者解析到同一个对象路径。

libbpf诊断信息

提交21dc508修改了libbpf错误日志处理方式。此前libbpf诊断信息可能直接输出到stderr,这次改为捕获日志并写入抛出的错误信息中。

该修改主要影响失败场景的可观测性。eBPF加载失败时,调用方可从异常消息中看到verifier或权限相关信息,而不是只能依赖外部标准错误输出。

Interceptor刷新接口

提交779f9b0给GumInterceptor新增两个刷新接口。

GUM_API gboolean gum_interceptor_flush_function(GumInterceptor * self,
    gconstpointer function_address);
GUM_API gboolean gum_interceptor_flush_listener(GumInterceptor * self,
    GumInvocationListener * listener);

gum_interceptor_flush()是全局刷新;新增的两个接口分别按函数地址和监听器刷新。源码中的实现会检查当前transaction状态和待销毁任务,只在指定函数或指定listener相关任务仍未完成时返回FALSE。

最小调用方式如下:

if (!gum_interceptor_flush_function (interceptor, target))
  g_print (\"target still busy\
\");

if (!gum_interceptor_flush_listener (interceptor, listener))
  g_print (\"listener still busy\
\");

该接口可供原生Gum使用者在卸载单个Hook或释放某个listener前确认相关调用已结束。

Windows arm64修复

17.12.0还包含多项Windows arm64相关修复。发布说明中列出的主要内容包括:

  1. stalker-arm64

修复远距离backpatch epilog丢失BL的问题。
2. stalker

在Windows上检测线程退出,避免继续跟踪进入ntdll线程清理路径。
3. exceptor

在Windows arm64上改用RtlRestoreContext()恢复上下文,避免longjmp()处理合成栈帧时终止进程。
4. CI增加Windows arm64原生任务。

这些修改集中在稳定性修复,不涉及新的脚本API。

公共API文档

17.12.0还补充了Gum和Core的公开API文档。发布说明中列出的Gum文档对象包括Interceptor、Stalker、Exceptor、Module、ModuleMap、ModuleRegistry、MemoryMap、MemoryAccessMonitor和DarwinGrafter。frida-core侧也新增了Vala文档注释注入GIR的相关提交。

这部分更新主要影响文档生成、GObject introspection数据和下游绑定维护。

总结

Frida17.12.0的更新包括以下几类:

  1. Gum新增函数范围定位和控制流图接口,GumJS增加对应的Process.findFunctionRange()和ControlFlowGraph。
  2. Linux注入器调整触发点采样、触发函数大小判断、触发点恢复和agent装载路径。
  3. Interceptor增加按函数和按listener刷新的接口。
  4. Windows arm64相关的Stalker、Exceptor和CI配置得到修复和补充。
  5. Gum和Core补充公开API文档和GObject introspection发布流程。

对脚本使用者来说,直接可用的新接口是Process.findFunctionRange()和ControlFlowGraph。对嵌入Gum或维护Frida集成的人来说,相关接口包括gum_process_find_function_range()、gum_control_flow_graph_*()和新的Interceptor刷新接口。Linux注入器相关修改属于内部实现调整,主要体现在attach和注入失败场景的行为变化。

参考

  1. Frida新闻页:https://frida.re/news/
  2. Frida17.12.0发布页:https://frida.re/news/2026/06/10/frida-17-12-0-released/
  3. frida-gum

提交67a1b9c、5e2fd31、b3273ea、f04c3e3、779f9b0
4. frida-core

提交a0a8838、33e5879、880f7ac、f0cc3b4、f51327f、21dc508


Frida17.12.0的功能与变化" 网络安全文章

Frida17.12.0的功能与变化"

Frida17.12.0主要更新包括新增函数范围定位接口Process.findFunctionRange()和控制流图分析模块ControlFlowGraph,提供基本块支配关系查询与代码形状分析能力;Linux注入器优化触发点采样策略并改进agent装载路径,提升attach稳定性;Interceptor增加细粒度刷新接口gum_interceptor_flush\\_function()。
Yakit热加载" 网络安全文章

Yakit热加载"

本文介绍Yakit中热加载技术的应用,该功能允许在WebFuzzer过程中动态加载Yak函数实现自定义fuzztag。作者通过实战案例演示如何利用热加载绕过RSA加密时间戳防重放机制:分析前端加密逻辑后编写热加载函数处理时间戳加密,最终成功实现字典爆破。文档包含具体函数定义、调用格式及注意事项。
特朗普计划提名SEC前主席出任情报总监" 网络安全文章

特朗普计划提名SEC前主席出任情报总监"

特朗普计划提名前SEC主席杰伊·克莱顿出任国家情报总监,以化解白宫与国会因比尔·普尔特任命引发的僵局。克莱顿具备华尔街法律精英背景与政治人脉,其提名被视为以专业性换取国会通过率的策略,核心目的是确保《外国情报监视法》第702条续期。该任命可能整合司法与情报力量,影响全球科技企业在美合规成本。
【HermesAgent】使用Hermes与Kali实现自动化渗透" 网络安全文章

【HermesAgent】使用Hermes与Kali实现自动化渗透"

本文介绍使用HermesAgent与Kali系统实现自动化渗透测试的完整方案,包括部署环境选择、模型配置、记忆模块设置和Skills技能包导入。重点推荐在Kali系统部署并选择低道德感模型,详细说明红队渗透专家角色设定和攻击流程规范,提供实战测试案例和技能管理方法。
评论:0   参与:  0